aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-05-16 20:43:57 +0800
committerGitHub <noreply@github.com>2018-05-16 20:43:57 +0800
commite67f0147998a9e3835ed3ce8bf6a0a0c634216c5 (patch)
treeb9c0b7d41cd9f78ae3404704a888da30e767edbe
parent124ca40dc525a987a88176c6e5170978e82fa290 (diff)
parent1e45d3ab2e0ca688c2ae48ab657f11496ccebc12 (diff)
downloaddexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.tar
dexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.tar.gz
dexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.tar.bz2
dexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.tar.lz
dexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.tar.xz
dexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.tar.zst
dexon-solidity-e67f0147998a9e3835ed3ce8bf6a0a0c634216c5.zip
Merge pull request #4148 from ethereum/develop
Merge develop into release for 0.4.24
-rw-r--r--.travis.yml2
-rw-r--r--CMakeLists.txt3
-rw-r--r--CODING_STYLE.md19
-rw-r--r--Changelog.md27
-rw-r--r--appveyor.yml9
-rw-r--r--circle.yml120
-rw-r--r--cmake/EthCompilerSettings.cmake5
-rw-r--r--cmake/jsoncpp.cmake22
-rw-r--r--docs/abi-spec.rst16
-rw-r--r--docs/assembly.rst272
-rw-r--r--docs/bugs_by_version.json4
-rw-r--r--docs/conf.py5
-rw-r--r--docs/contracts.rst46
-rw-r--r--docs/control-structures.rst31
-rw-r--r--docs/frequently-asked-questions.rst4
-rw-r--r--docs/grammar.txt4
-rw-r--r--docs/installing-solidity.rst43
-rw-r--r--docs/introduction-to-smart-contracts.rst2
-rw-r--r--docs/julia.rst56
-rw-r--r--docs/requirements.txt1
-rw-r--r--docs/security-considerations.rst2
-rw-r--r--docs/solidity-by-example.rst18
-rw-r--r--docs/structure-of-a-contract.rst2
-rw-r--r--docs/style-guide.rst9
-rw-r--r--docs/units-and-global-variables.rst9
-rw-r--r--docs/using-the-compiler.rst11
-rw-r--r--libdevcore/JSON.cpp4
-rw-r--r--libevmasm/Assembly.h4
-rw-r--r--libevmasm/AssemblyItem.cpp16
-rw-r--r--libevmasm/CMakeLists.txt2
-rw-r--r--libevmasm/ConstantOptimiser.h1
-rw-r--r--libevmasm/Instruction.h14
-rw-r--r--libevmasm/PathGasMeter.cpp19
-rw-r--r--libevmasm/PathGasMeter.h10
-rw-r--r--libevmasm/PeepholeOptimiser.h2
-rw-r--r--libevmasm/RuleList.h20
-rw-r--r--libjulia/Exceptions.h35
-rw-r--r--libjulia/optimiser/ASTCopier.cpp11
-rw-r--r--libjulia/optimiser/ASTCopier.h3
-rw-r--r--libjulia/optimiser/ASTWalker.cpp2
-rw-r--r--libjulia/optimiser/ASTWalker.h16
-rw-r--r--libjulia/optimiser/CommonSubexpressionEliminator.cpp3
-rw-r--r--libjulia/optimiser/DataFlowAnalyzer.cpp8
-rw-r--r--libjulia/optimiser/Disambiguator.cpp24
-rw-r--r--libjulia/optimiser/Disambiguator.h3
-rw-r--r--libjulia/optimiser/ExpressionInliner.h2
-rw-r--r--libjulia/optimiser/ExpressionSimplifier.cpp2
-rw-r--r--libjulia/optimiser/FullInliner.cpp259
-rw-r--r--libjulia/optimiser/FullInliner.h179
-rw-r--r--libjulia/optimiser/FunctionGrouper.cpp2
-rw-r--r--libjulia/optimiser/FunctionHoister.cpp2
-rw-r--r--libjulia/optimiser/InlinableExpressionFunctionFinder.cpp6
-rw-r--r--libjulia/optimiser/InlinableExpressionFunctionFinder.h2
-rw-r--r--libjulia/optimiser/MainFunction.cpp54
-rw-r--r--libjulia/optimiser/MainFunction.h41
-rw-r--r--libjulia/optimiser/NameCollector.cpp1
-rw-r--r--libjulia/optimiser/NameCollector.h9
-rw-r--r--libjulia/optimiser/NameDispenser.cpp38
-rw-r--r--libjulia/optimiser/NameDispenser.h37
-rw-r--r--libjulia/optimiser/README.md9
-rw-r--r--libjulia/optimiser/Rematerialiser.cpp3
-rw-r--r--libjulia/optimiser/Semantics.cpp4
-rw-r--r--libjulia/optimiser/SyntacticalEquality.cpp5
-rw-r--r--libjulia/optimiser/UnusedPruner.cpp5
-rw-r--r--libjulia/optimiser/Utilities.h5
-rw-r--r--libsolidity/CMakeLists.txt2
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.cpp156
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.h52
-rw-r--r--libsolidity/analysis/ControlFlowBuilder.cpp370
-rw-r--r--libsolidity/analysis/ControlFlowBuilder.h143
-rw-r--r--libsolidity/analysis/ControlFlowGraph.cpp136
-rw-r--r--libsolidity/analysis/ControlFlowGraph.h148
-rw-r--r--libsolidity/analysis/TypeChecker.cpp160
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/ast/ASTVisitor.h2
-rw-r--r--libsolidity/ast/Types.cpp64
-rw-r--r--libsolidity/ast/Types.h50
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp102
-rw-r--r--libsolidity/codegen/ABIFunctions.h8
-rw-r--r--libsolidity/codegen/CompilerContext.h3
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp34
-rw-r--r--libsolidity/codegen/CompilerUtils.h2
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp20
-rw-r--r--libsolidity/codegen/LValue.cpp2
-rw-r--r--libsolidity/codegen/LValue.h1
-rw-r--r--libsolidity/formal/SMTChecker.cpp62
-rw-r--r--libsolidity/formal/SMTChecker.h6
-rw-r--r--libsolidity/formal/SolverInterface.h1
-rw-r--r--libsolidity/formal/SymbolicVariable.h1
-rw-r--r--libsolidity/formal/VariableUsage.cpp1
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp10
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp8
-rw-r--r--libsolidity/interface/CompilerStack.cpp18
-rw-r--r--libsolidity/interface/GasEstimator.cpp19
-rw-r--r--libsolidity/parsing/DocStringParser.cpp2
-rw-r--r--libsolidity/parsing/Parser.cpp308
-rw-r--r--libsolidity/parsing/Parser.h42
-rw-r--r--libsolidity/parsing/ParserBase.cpp54
-rw-r--r--libsolidity/parsing/ParserBase.h2
-rw-r--r--libsolidity/parsing/Token.h11
-rw-r--r--lllc/CMakeLists.txt2
-rwxr-xr-xscripts/create_source_tarball.sh2
-rwxr-xr-xscripts/install_cmake.sh2
-rw-r--r--scripts/release.bat14
-rwxr-xr-xscripts/release_ppa.sh2
-rwxr-xr-xscripts/tests.sh29
-rwxr-xr-xscripts/travis-emscripten/build_emscripten.sh10
-rw-r--r--solc/CommandLineInterface.cpp3
-rw-r--r--std/StandardToken.sol6
-rw-r--r--std/Token.sol6
-rw-r--r--test/ExecutionFramework.h1
-rw-r--r--test/libevmasm/Optimiser.cpp69
-rw-r--r--test/libjulia/Inliner.cpp166
-rw-r--r--test/libjulia/MainFunction.cpp87
-rw-r--r--test/libjulia/Parser.cpp10
-rw-r--r--test/libsolidity/AnalysisFramework.h1
-rw-r--r--test/libsolidity/Assembly.cpp5
-rw-r--r--test/libsolidity/GasMeter.cpp40
-rw-r--r--test/libsolidity/InlineAssembly.cpp26
-rw-r--r--test/libsolidity/SMTChecker.cpp76
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp309
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp188
-rw-r--r--test/libsolidity/SolidityParser.cpp487
-rw-r--r--test/libsolidity/StandardCompiler.cpp6
-rw-r--r--test/libsolidity/syntaxTests/constants/abi_encoding_constant.sol7
-rw-r--r--test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol6
-rw-r--r--test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol8
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol2
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol5
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol26
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol10
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol19
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol36
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol35
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol6
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol15
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/for_fine.sol13
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol16
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/if_fine.sol29
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol18
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_fine.sol20
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol22
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_fine.sol12
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_fine.sol11
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol18
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke.sol10
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_fine.sol14
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol13
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol9
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol12
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/while_fine.sol19
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol11
-rw-r--r--test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol5
-rw-r--r--test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol6
-rw-r--r--test/libsolidity/syntaxTests/denominations/denominations.sol7
-rw-r--r--test/libsolidity/syntaxTests/denominations/deprecated_year.sol5
-rw-r--r--test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol6
-rw-r--r--test/libsolidity/syntaxTests/denominations/fixed_point_division.sol6
-rw-r--r--test/libsolidity/syntaxTests/deprecated_functions.sol12
-rw-r--r--test/libsolidity/syntaxTests/deprecated_functions_050.sol15
-rw-r--r--test/libsolidity/syntaxTests/emit_non_event.sol10
-rw-r--r--test/libsolidity/syntaxTests/empty_string_var.sol11
-rw-r--r--test/libsolidity/syntaxTests/fallback/pure_modifier.sol6
-rw-r--r--test/libsolidity/syntaxTests/fallback/view_modifier.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol2
-rw-r--r--test/libsolidity/syntaxTests/inheritance/override/add_view.sol4
-rw-r--r--test/libsolidity/syntaxTests/inheritance/override/remove_view.sol4
-rw-r--r--test/libsolidity/syntaxTests/missing_state_variable.sol7
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol6
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol11
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol8
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol9
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol12
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol13
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol12
-rw-r--r--test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol9
-rw-r--r--test/libsolidity/syntaxTests/natspec/docstring_empty_description.sol (renamed from test/libsolidity/syntaxTests/docstring_empty_description.sol)0
-rw-r--r--test/libsolidity/syntaxTests/natspec/docstring_empty_tag.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/comment_end_with_double_star.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/constant_is_keyword.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol7
-rw-r--r--test/libsolidity/syntaxTests/parsing/emit_without_event.sol8
-rw-r--r--test/libsolidity/syntaxTests/parsing/empty_comment.sol3
-rw-r--r--test/libsolidity/syntaxTests/parsing/empty_enum.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/empty_function.sol10
-rw-r--r--test/libsolidity/syntaxTests/parsing/event_with_no_argument_list.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/external_variable.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/fixed_literal_with_double_radix.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/function_no_body.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_modifiers.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/function_type_state_variable.sol4
-rw-r--r--test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_lvalue.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_without_lvalue.sol8
-rw-r--r--test/libsolidity/syntaxTests/parsing/interface_basic.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol7
-rw-r--r--test/libsolidity/syntaxTests/parsing/local_const_variable.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/location_specifiers_with_var.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/malformed_enum_declaration.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/missing_argument_in_named_args.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/missing_parameter_name_in_named_args.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol2
-rw-r--r--test/libsolidity/syntaxTests/parsing/modifier_without_semicolon.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol29
-rw-r--r--test/libsolidity/syntaxTests/parsing/multiple_function_param_trailing_comma.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/multiple_statemutability_specifiers.sol33
-rw-r--r--test/libsolidity/syntaxTests/parsing/new_invalid_type_name.sol7
-rw-r--r--test/libsolidity/syntaxTests/parsing/no_function_params.sol7
-rw-r--r--test/libsolidity/syntaxTests/parsing/overloaded_functions.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/payable_accessor.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/return_var.sol28
-rw-r--r--test/libsolidity/syntaxTests/parsing/scientific_notation.sol7
-rw-r--r--test/libsolidity/syntaxTests/parsing/single_event_arg_trailing_comma.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/single_function_param.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/single_function_param_trailing_comma.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/single_modifier_arg_trailing_comma.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/single_return_param_trailing_comma.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/trailing_comma_in_named_args.sol6
-rw-r--r--test/libsolidity/syntaxTests/parsing/tuples.sol24
-rw-r--r--test/libsolidity/syntaxTests/parsing/tuples_without_commas.sol7
-rw-r--r--test/libsolidity/syntaxTests/parsing/var_array.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol28
-rw-r--r--test/libsolidity/syntaxTests/parsing/var_storage_var.sol2
-rw-r--r--test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol7
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol15
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol10
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol1
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol1
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol2
-rw-r--r--test/libsolidity/syntaxTests/tight_packing_literals.sol8
-rw-r--r--test/libsolidity/syntaxTests/tight_packing_literals_050.sol34
-rw-r--r--test/libsolidity/syntaxTests/tight_packing_literals_fine.sol25
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol12
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol24
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol8
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol8
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol10
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol11
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol11
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol9
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol10
-rw-r--r--test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol10
-rw-r--r--test/libsolidity/syntaxTests/types/bytes0.sol5
-rw-r--r--test/libsolidity/syntaxTests/types/bytes256.sol5
-rw-r--r--test/libsolidity/syntaxTests/types/bytes33.sol5
-rw-r--r--test/libsolidity/syntaxTests/types/bytesm.sol36
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_event.sol10
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol10
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_function.sol12
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol11
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol13
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol11
-rw-r--r--test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol11
-rw-r--r--test/tools/CMakeLists.txt2
254 files changed, 4902 insertions, 1598 deletions
diff --git a/.travis.yml b/.travis.yml
index 3640d2e2..8487deef 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -173,7 +173,7 @@ before_script:
&& scripts/create_source_tarball.sh)
script:
- - test $SOLC_EMSCRIPTEN != On || (scripts/test_emscripten.sh)
+ - test $SOLC_EMSCRIPTEN != On -o $SOLC_TESTS != On || (scripts/test_emscripten.sh)
- test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh)
- test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e6e177ac..0c05208f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -8,7 +8,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
-set(PROJECT_VERSION "0.4.23")
+set(PROJECT_VERSION "0.4.24")
project(solidity VERSION ${PROJECT_VERSION})
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
@@ -21,6 +21,7 @@ include(EthCcache)
# Let's find our dependencies
include(EthDependencies)
include(jsoncpp)
+include_directories(SYSTEM ${JSONCPP_INCLUDE_DIR})
find_package(Threads)
diff --git a/CODING_STYLE.md b/CODING_STYLE.md
index 3244466b..f36503d0 100644
--- a/CODING_STYLE.md
+++ b/CODING_STYLE.md
@@ -49,21 +49,28 @@ cout << "some very long string that contains completely irrelevant text that tal
## 1. Namespaces
1. No `using namespace` declarations in header files.
-2. All symbols should be declared in a namespace except for final applications.
-3. Use anonymous namespaces for helpers whose scope is a cpp file only.
-4. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore.
+2. Use `using namespace std;` in cpp files, but avoid importing namespaces from boost and others.
+3. All symbols should be declared in a namespace except for final applications.
+4. Use anonymous namespaces for helpers whose scope is a cpp file only.
+5. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore.
-Yes:
+Only in the header:
```cpp
#include <cassert>
+namespace myNamespace
+{
std::tuple<float, float> meanAndSigma(std::vector<float> const& _v);
+}
```
-No:
+Only in the cpp file:
```cpp
#include <cassert>
using namespace std;
-tuple<float, float> meanAndSigma(vector<float> const& _v);
+tuple<float, float> myNamespace::meanAndSigma(vector<float> const& _v)
+{
+ // ...
+}
```
## 2. Preprocessor
diff --git a/Changelog.md b/Changelog.md
index 4cfa4385..6a408ae8 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,30 @@
+### 0.4.24 (2018-05-16)
+
+Language Features:
+ * Code Generator: Use native shift instructions on target Constantinople.
+ * General: Allow multiple variables to be declared as part of a tuple assignment, e.g. ``(uint a, uint b) = ...``.
+ * General: Remove deprecated ``constant`` as function state modifier from documentation and tests (but still leave it as a valid feature).
+ * Type Checker: Deprecate the ``years`` unit denomination and raise a warning for it (or an error as experimental 0.5.0 feature).
+ * Type Checker: Make literals (without explicit type casting) an error for tight packing as experimental 0.5.0 feature.
+ * Type Checker: Warn about wildcard tuple assignments (this will turn into an error with version 0.5.0).
+ * Type Checker: Warn when ``keccak256``, ``sha256`` and ``ripemd160`` are not used with a single bytes argument (suggest to use ``abi.encodePacked(...)``). This will turn into an error with version 0.5.0.
+
+Compiler Features:
+ * Build System: Update internal dependency of jsoncpp to 1.8.4, which introduces more strictness and reduces memory usage.
+ * Control Flow Graph: Add Control Flow Graph as analysis structure.
+ * Control Flow Graph: Warn about returning uninitialized storage pointers.
+ * Gas Estimator: Only explore paths with higher gas costs. This reduces accuracy but greatly improves the speed of gas estimation.
+ * Optimizer: Remove unnecessary masking of the result of known short instructions (``ADDRESS``, ``CALLER``, ``ORIGIN`` and ``COINBASE``).
+ * Parser: Display nicer error messages by showing the actual tokens and not internal names.
+ * Parser: Use the entire location of the token instead of only its starting position as source location for parser errors.
+ * SMT Checker: Support state variables of integer and bool type.
+
+Bugfixes:
+ * Code Generator: Fix ``revert`` with reason coming from a state or local string variable.
+ * Type Checker: Show proper error when trying to ``emit`` a non-event.
+ * Type Checker: Warn about empty tuple components (this will turn into an error with version 0.5.0).
+ * Type Checker: The ABI encoding functions are pure and thus can be used for constants.
+
### 0.4.23 (2018-04-19)
Features:
diff --git a/appveyor.yml b/appveyor.yml
index 5fd85482..81ec711d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -31,10 +31,12 @@ branches:
only:
- release
- develop
-os: Visual Studio 2015
configuration:
- RelWithDebInfo
environment:
+ matrix:
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+ - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
# This is used for pushing to solidity-test-bytecodes
priv_key:
secure: yYGwg4rhCdHfwuv2mFjaNEDwAx3IKUbp0D5fMGpaKefnfk+BiMS5bqSHRiOj91PZ91P9pUk2Vu+eNuS4hTFCf1zFGfrOhlJ4Ij0xSyU5m/LQr590Mo+f7W94Xc8ubgo6j2hp9qH/szTqTzmAkmxKO5TLlWjVzVny2t/s5o5UprLS1/MdzDNLjpVNXR03oKfdWUV9a2l6+PejXCbqyUCagh6BByZqeAPbDcil6eAfxu4EPX83Fuurof+KqFzIWycBG5qK1pTipn2pxiA0QKuUrD8y8VNL0S23NTgxoxSp7nPVMd3K0qRSzPM5lrqS7Z8i3evkVwPbuhu0gSiV08jGVahH2snQ3JGYsH2D4KmVn/xiVBeJ0lRplYlfZF0GUu7iJ+DDxi6wBPhW9A25/NyD/mx7Ub2dLheyWi8AjdSCzhfRD+4We8FQQeHRo3Q0kAohFmlCXdXhrcwOOloId8r6xYwg+hWxHTt2Oe9CKwXfmiPjgl/Gd6lYgLpyyfJ8drQ6tjO/pybLEa10v74qYNdVW5LaLIsRUM9Jm/FDVTrOGYtPndi87mF+/tBJIaXXNz0EMl5xvsKW0SBfUMV49zoDDKZZgWyO9U/cfViEUi7Sdn9QLsBWLZfSgBQNkq3WGZVKPq58OxEWT9dUghQHlSVh2qWF/NUx0TRBjiJl9JM56ENTMD00y18eDcXNCeLLVYB+R1axabUPdXivrO+BrWQK94IWxKEJ+YYN8WVJWAO5T/EBDKwgiXGneePwJ75WP7XCLtuYxqjC+CeW3xBVCzCEeZB/VVBvt7fhmtcoeZZ6tAS10h0yY5WWZ/EUVorj+c/FrMm7Nlpcrd1p4hciffePSLVg+yvy9/xTuM9trYWMgj4xcDQbYsaeItHO2Z3EiUoCgNdUw6rONiNwS/XBApWhCcklWm0/g62h2gOa7/hnKG6p2omQzYw+cOzWbF9+DBzoTSXXZXqbUshVee+CD+iYJKleGYSdbMdM89HW4HyskHk6HgM1ggE8CsgD1pMhXtqLTYZBlvsZCBkHPkD9NhGD2DtrNOmJOW8xwkL2/Il6roDF4n856XNdsjvd++rvQoKr58SkyApCJeCo3sfVres0W22g+7If2b2kWC4/DphrFkeaceFzJOctBUrwstvQBXIVOcadU978A3E7jvTaMR4JL9kC/iPOUVNjNRNM/gNvTlf3CIyMMszFeftjEBGnCZaSpht2RtNapRQQb6QPkOP88nufQVZq/TP1ECmvdTUWJ7kSnAupu6u8oH2x2IIm/KKeIwSYU5rGxjRb36DwgXCHcwfRYo3VNorwTeZGj4q1TSM9PuvgzNg//gKZW6VRa+HdNm/40ZGpDsOrr55tOBqfpq9k5RmevqW/OMZS3xUuArKdYLQY75t9eWcbHSgFN2ZY1KEdyEEvVKgs6Q4lEnSSulGxroRxTU5BOoA0V4tCeCUoSPD3FB93WsO9fBPzNsqOuBtDdIkApefzc1pT38uKpmVfggKUsoWUdqMXAWqCDWr2uw9EE900RJpEY6mIEWhkcro5LAMwaqByOGpqFFUkH+UWTC102eVHEmjxKpC6c6cSzoKKU6Ckd+jVRFO7TvmVe1MKCwjXj8lcAfAM2gQ+XehtrQdIBhAmCrnzurfz2u9tKVdpiADC1ig+kMs1/HX2713LYVXzDKdk+duQ94SVtGv9F2Iv+KN5oq4UFgll6VGt7GHsJOrYYf/wrOfB09IkpmjNygvcpmmSdcXXF8ulDD6KHTGEGUlFwLOpEwKx+zX2ZvviStHhN8KsoTKSVSueDmSSI63HdTS7FxfrHJc1yAzsdqEN5g5eV/z2Fn34qy64mdFSAZMF5zsbWZYFpc9ef3llF5aRcuD90JWT2VC7rB2jeGEtiwGkDlqKzxqRvJk06wTK6+n5RncN66bDaksulOPJMAR/bRW7dinV8T6yIvybuhqDetxJQP6eyAnW4xr1YxIAG4BXGZV6XAPTgOG2oGvMdncxkcLQHXVu07x39ySqP/m2MBxn0zF3DmaqrSPIRMhS8gG3d/23Jux3YHDEOBHjdJSdwqs5F5+QBFPV2rmJnpcSoW4d3M119XI20L914c62R7wY4e6+qmi3ydQU9g6p8psZgaE3TuMsyzX3k4C30nC/3gWT+zl253NjZwfbzIdHu5LWNDY9kEHtKzLP
@@ -62,11 +64,12 @@ install:
before_build:
- if not exist build mkdir build
- cd build
- - cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" ( cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On )
+ else ( cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On )
build_script:
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
- cd %APPVEYOR_BUILD_FOLDER%
- - scripts\release.bat %CONFIGURATION%
+ - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" ( scripts\release.bat %CONFIGURATION% 2017 )
- ps: $bytecodedir = git show -s --format="%cd-%H" --date=short
test_script:
diff --git a/circle.yml b/circle.yml
index f97b619a..4ce3082b 100644
--- a/circle.yml
+++ b/circle.yml
@@ -4,12 +4,38 @@ defaults:
filters:
tags:
only: /.*/
+ - setup_prerelease_commit_hash: &setup_prerelease_commit_hash
+ name: Store commit hash and prerelease
+ command: |
+ if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
+ echo -n "$CIRCLE_SHA1" > commit_hash.txt
+ - run_build: &run_build
+ name: Build
+ command: |
+ mkdir -p build
+ cd build
+ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ make -j4
+ - run_tests: &run_tests
+ name: Tests
+ command: scripts/tests.sh --junit_report test_results
+ - solc_artifact: &solc_artifact
+ path: build/solc/solc
+ destination: solc
+ - all_artifacts: &all_artifacts
+ root: build
+ paths:
+ - solc/solc
+ - test/soltest
+ - test/tools/solfuzzer
version: 2
jobs:
build_emscripten:
docker:
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
+ environment:
+ TERM: xterm
steps:
- checkout
- restore_cache:
@@ -42,6 +68,8 @@ jobs:
test_emscripten_solcjs:
docker:
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
+ environment:
+ TERM: xterm
steps:
- checkout
- attach_workspace:
@@ -66,6 +94,8 @@ jobs:
test_emscripten_external:
docker:
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
+ environment:
+ TERM: xterm
steps:
- checkout
- attach_workspace:
@@ -87,9 +117,11 @@ jobs:
command: |
. /usr/local/nvm/nvm.sh
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
- build_x86:
+ build_x86_linux:
docker:
- image: buildpack-deps:artful
+ environment:
+ TERM: xterm
steps:
- checkout
- run:
@@ -97,31 +129,37 @@ jobs:
command: |
apt-get -qq update
apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev
+ - run: *setup_prerelease_commit_hash
+ - run: *run_build
+ - store_artifacts: *solc_artifact
+ - persist_to_workspace: *all_artifacts
+
+ build_x86_mac:
+ macos:
+ xcode: "9.0"
+ environment:
+ TERM: xterm
+ steps:
+ - checkout
- run:
- name: Store commit hash and prerelease
- command: |
- if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
- echo -n "$CIRCLE_SHA1" > commit_hash.txt
- - run:
- name: Build
+ name: Install build dependencies
command: |
- mkdir -p build
- cd build
- cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
- make -j4
- - store_artifacts:
- path: build/solc/solc
- destination: solc
- - persist_to_workspace:
- root: build
- paths:
- - solc/solc
- - test/soltest
- - test/tools/solfuzzer
+ brew update
+ brew upgrade
+ brew unlink python
+ brew install z3
+ brew install boost
+ brew install cmake
+ - run: *setup_prerelease_commit_hash
+ - run: *run_build
+ - store_artifacts: *solc_artifact
+ - persist_to_workspace: *all_artifacts
- test_x86:
+ test_x86_linux:
docker:
- image: buildpack-deps:artful
+ environment:
+ TERM: xterm
steps:
- checkout
- attach_workspace:
@@ -132,9 +170,28 @@ jobs:
apt-get -qq update
apt-get -qy install libz3-dev libleveldb1v5
- run: mkdir -p test_results
+ - run: *run_tests
+ - store_test_results:
+ path: test_results/
+
+ test_x86_mac:
+ macos:
+ xcode: "9.0"
+ environment:
+ TERM: xterm
+ steps:
+ - checkout
+ - attach_workspace:
+ at: build
- run:
- name: Tests
- command: scripts/tests.sh --junit_report test_results
+ name: Install dependencies
+ command: |
+ brew update
+ brew upgrade
+ brew unlink python
+ brew install z3
+ - run: mkdir -p test_results
+ - run: *run_tests
- store_test_results:
path: test_results/
@@ -148,11 +205,7 @@ jobs:
command: |
apt-get -qq update
apt-get -qy install python-sphinx
- - run:
- name: Store commit hash and prerelease
- command: |
- if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
- echo -n "$CIRCLE_SHA1" > commit_hash.txt
+ - run: *setup_prerelease_commit_hash
- run:
name: Build documentation
command: ./scripts/docs.sh
@@ -173,9 +226,14 @@ workflows:
<<: *build_on_tags
requires:
- build_emscripten
- - build_x86: *build_on_tags
- - test_x86:
+ - build_x86_linux: *build_on_tags
+ - build_x86_mac: *build_on_tags
+ - test_x86_linux:
+ <<: *build_on_tags
+ requires:
+ - build_x86_linux
+ - test_x86_mac:
<<: *build_on_tags
requires:
- - build_x86
+ - build_x86_mac
- docs: *build_on_tags
diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake
index c73536ad..683d1d2e 100644
--- a/cmake/EthCompilerSettings.cmake
+++ b/cmake/EthCompilerSettings.cmake
@@ -62,8 +62,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
# Additional Clang-specific compiler settings.
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
- # Set stack size to 16MB - by default Apple's clang defines a stack size of 8MB, some tests require more.
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size -Wl,0x1000000")
+ # Set stack size to 32MB - by default Apple's clang defines a stack size of 8MB.
+ # Normally 16MB is enough to run all tests, but it will exceed the stack, if -DSANITIZE=address is used.
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size -Wl,0x2000000")
endif()
# Some Linux-specific Clang settings. We don't want these for OS X.
diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake
index 6ddf4c74..3d6b37ed 100644
--- a/cmake/jsoncpp.cmake
+++ b/cmake/jsoncpp.cmake
@@ -6,17 +6,15 @@ else()
set(JSONCPP_CMAKE_COMMAND ${CMAKE_COMMAND})
endif()
-# Disable implicit fallthrough warning in jsoncpp for gcc >= 7 until the upstream handles it properly
-if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)
- set(JSONCCP_EXTRA_FLAGS -Wno-implicit-fallthrough)
-else()
- set(JSONCCP_EXTRA_FLAGS "")
-endif()
-
+include(GNUInstallDirs)
set(prefix "${CMAKE_BINARY_DIR}/deps")
-set(JSONCPP_LIBRARY "${prefix}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}jsoncpp${CMAKE_STATIC_LIBRARY_SUFFIX}")
+set(JSONCPP_LIBRARY "${prefix}/${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}jsoncpp${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(JSONCPP_INCLUDE_DIR "${prefix}/include")
+if(NOT MSVC)
+ set(JSONCPP_EXTRA_FLAGS "-std=c++11")
+endif()
+
set(byproducts "")
if(CMAKE_VERSION VERSION_GREATER 3.1)
set(byproducts BUILD_BYPRODUCTS "${JSONCPP_LIBRARY}")
@@ -25,9 +23,9 @@ endif()
ExternalProject_Add(jsoncpp-project
PREFIX "${prefix}"
DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads"
- DOWNLOAD_NAME jsoncpp-1.7.7.tar.gz
- URL https://github.com/open-source-parsers/jsoncpp/archive/1.7.7.tar.gz
- URL_HASH SHA256=087640ebcf7fbcfe8e2717a0b9528fff89c52fcf69fa2a18cc2b538008098f97
+ DOWNLOAD_NAME jsoncpp-1.8.4.tar.gz
+ URL https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz
+ URL_HASH SHA256=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6
CMAKE_COMMAND ${JSONCPP_CMAKE_COMMAND}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
@@ -36,7 +34,7 @@ ExternalProject_Add(jsoncpp-project
-DCMAKE_POSITION_INDEPENDENT_CODE=${BUILD_SHARED_LIBS}
-DJSONCPP_WITH_TESTS=OFF
-DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF
- -DCMAKE_CXX_FLAGS=${JSONCCP_EXTRA_FLAGS}
+ -DCMAKE_CXX_FLAGS=${JSONCPP_EXTRA_FLAGS}
# Overwrite build and install commands to force Release build on MSVC.
BUILD_COMMAND cmake --build <BINARY_DIR> --config Release
INSTALL_COMMAND cmake --build <BINARY_DIR> --config Release --target install
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 98301fdc..8591a07f 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -62,7 +62,7 @@ The following elementary types exist:
The following (fixed-size) array type exists:
-- ``<type>[M]``: a fixed-length array of ``M`` elements, ``M > 0``, of the given type.
+- ``<type>[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type.
The following non-fixed-size types exist:
@@ -77,7 +77,7 @@ of them inside parentheses, separated by commas:
- ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0``
-It is possible to form tuples of tuples, arrays of tuples and so on.
+It is possible to form tuples of tuples, arrays of tuples and so on. It is also possible to form zero-tuples (where ``n == 0``).
.. note::
Solidity supports all the types presented above with the same names with the exception of tuples. The ABI tuple type is utilised for encoding Solidity ``structs``.
@@ -101,8 +101,8 @@ We distinguish static and dynamic types. Static types are encoded in-place and d
* ``bytes``
* ``string``
* ``T[]`` for any ``T``
-* ``T[k]`` for any dynamic ``T`` and any ``k > 0``
-* ``(T1,...,Tk)`` if any ``Ti`` is dynamic for ``1 <= i <= k``
+* ``T[k]`` for any dynamic ``T`` and any ``k >= 0``
+* ``(T1,...,Tk)`` if ``Ti`` is dynamic for some ``1 <= i <= k``
All other types are called "static".
@@ -117,16 +117,16 @@ on the type of ``X`` being
- ``(T1,...,Tk)`` for ``k >= 0`` and any types ``T1``, ..., ``Tk``
- ``enc(X) = head(X(1)) ... head(X(k-1)) tail(X(0)) ... tail(X(k-1))``
+ ``enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))``
- where ``X(i)`` is the ``ith`` component of the value, and
+ where ``X = (X(1), ..., X(k))`` and
``head`` and ``tail`` are defined for ``Ti`` being a static type as
``head(X(i)) = enc(X(i))`` and ``tail(X(i)) = ""`` (the empty string)
and as
- ``head(X(i)) = enc(len(head(X(0)) ... head(X(k-1)) tail(X(0)) ... tail(X(i-1))))``
+ ``head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1)) ))``
``tail(X(i)) = enc(X(i))``
otherwise, i.e. if ``Ti`` is a dynamic type.
@@ -144,7 +144,7 @@ on the type of ``X`` being
- ``T[]`` where ``X`` has ``k`` elements (``k`` is assumed to be of type ``uint256``):
- ``enc(X) = enc(k) enc([X[1], ..., X[k]])``
+ ``enc(X) = enc(k) enc([X[0], ..., X[k-1]])``
i.e. it is encoded as if it were an array of static size ``k``, prefixed with
the number of elements.
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 978e71e3..443cb7da 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -418,6 +418,9 @@ changes during the call, and thus references to local variables will be wrong.
Labels
------
+.. note::
+ Labels are deprecated. Please use functions, loops, if or switch statements instead.
+
Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses
which can change easily. Solidity inline assembly provides labels to make the use of
jumps easier. Note that labels are a low-level feature and it is possible to write
@@ -519,6 +522,10 @@ is performed by replacing the variable's value on the stack by the new value.
=: v // instruction style assignment, puts the result of sload(10) into v
}
+.. note::
+ Instruction-style assignment is deprecated.
+
+
If
--
@@ -693,9 +700,9 @@ the form ``mul(add(x, y), 7)`` are preferred over pure opcode statements like
``7 y x add mul`` because in the first form, it is much easier to see which
operand is used for which opcode.
-The second goal is achieved by introducing a desugaring phase that only removes
-the higher level constructs in a very regular way and still allows inspecting
-the generated low-level assembly code. The only non-local operation performed
+The second goal is achieved by compiling the
+higher level constructs to bytecode in a very regular way.
+The only non-local operation performed
by the assembler is name lookup of user-defined identifiers (functions, variables, ...),
which follow very simple and regular scoping rules and cleanup of local variables from the stack.
@@ -716,8 +723,6 @@ keep track of the current so-called stack height. Since all local variables
are removed at the end of a block, the stack height before and after the block
should be the same. If this is not the case, a warning is issued.
-Why do we use higher-level constructs like ``switch``, ``for`` and functions:
-
Using ``switch``, ``for`` and functions, it should be possible to write
complex code without using ``jump`` or ``jumpi`` manually. This makes it much
easier to analyze the control flow, which allows for improved formal
@@ -726,13 +731,11 @@ verification and optimization.
Furthermore, if manual jumps are allowed, computing the stack height is rather complicated.
The position of all local variables on the stack needs to be known, otherwise
neither references to local variables nor removing local variables automatically
-from the stack at the end of a block will work properly. The desugaring
-mechanism correctly inserts operations at unreachable blocks that adjust the
-stack height properly in case of jumps that do not have a continuing control flow.
+from the stack at the end of a block will work properly.
Example:
-We will follow an example compilation from Solidity to desugared assembly.
+We will follow an example compilation from Solidity to assembly.
We consider the runtime bytecode of the following Solidity program::
pragma solidity ^0.4.16;
@@ -772,99 +775,9 @@ The following assembly will be generated::
}
}
-After the desugaring phase it looks as follows::
-
- {
- mstore(0x40, 0x60)
- {
- let $0 := div(calldataload(0), exp(2, 226))
- jumpi($case1, eq($0, 0xb3de648b))
- jump($caseDefault)
- $case1:
- {
- // the function call - we put return label and arguments on the stack
- $ret1 calldataload(4) jump(f)
- // This is unreachable code. Opcodes are added that mirror the
- // effect of the function on the stack height: Arguments are
- // removed and return values are introduced.
- pop pop
- let r := 0
- $ret1: // the actual return point
- $ret2 0x20 jump($allocate)
- pop pop let ret := 0
- $ret2:
- mstore(ret, r)
- return(ret, 0x20)
- // although it is useless, the jump is automatically inserted,
- // since the desugaring process is a purely syntactic operation that
- // does not analyze control-flow
- jump($endswitch)
- }
- $caseDefault:
- {
- revert(0, 0)
- jump($endswitch)
- }
- $endswitch:
- }
- jump($afterFunction)
- allocate:
- {
- // we jump over the unreachable code that introduces the function arguments
- jump($start)
- let $retpos := 0 let size := 0
- $start:
- // output variables live in the same scope as the arguments and is
- // actually allocated.
- let pos := 0
- {
- pos := mload(0x40)
- mstore(0x40, add(pos, size))
- }
- // This code replaces the arguments by the return values and jumps back.
- swap1 pop swap1 jump
- // Again unreachable code that corrects stack height.
- 0 0
- }
- f:
- {
- jump($start)
- let $retpos := 0 let x := 0
- $start:
- let y := 0
- {
- let i := 0
- $for_begin:
- jumpi($for_end, iszero(lt(i, x)))
- {
- y := mul(2, y)
- }
- $for_continue:
- { i := add(i, 1) }
- jump($for_begin)
- $for_end:
- } // Here, a pop instruction will be inserted for i
- swap1 pop swap1 jump
- 0 0
- }
- $afterFunction:
- stop
- }
-
-
-Assembly happens in four stages:
-1. Parsing
-2. Desugaring (removes switch, for and functions)
-3. Opcode stream generation
-4. Bytecode generation
-
-We will specify steps one to three in a pseudo-formal way. More formal
-specifications will follow.
-
-
-Parsing / Grammar
------------------
+Assembly Grammar
+----------------
The tasks of the parser are the following:
@@ -922,160 +835,3 @@ Grammar::
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
HexNumber = '0x' [0-9a-fA-F]+
DecimalNumber = [0-9]+
-
-
-Desugaring
-----------
-
-An AST transformation removes for, switch and function constructs. The result
-is still parseable by the same parser, but it will not use certain constructs.
-If jumpdests are added that are only jumped to and not continued at, information
-about the stack content is added, unless no local variables of outer scopes are
-accessed or the stack height is the same as for the previous instruction.
-
-Pseudocode::
-
- desugar item: AST -> AST =
- match item {
- AssemblyFunctionDefinition('function' name '(' arg1, ..., argn ')' '->' ( '(' ret1, ..., retm ')' body) ->
- <name>:
- {
- jump($<name>_start)
- let $retPC := 0 let argn := 0 ... let arg1 := 0
- $<name>_start:
- let ret1 := 0 ... let retm := 0
- { desugar(body) }
- swap and pop items so that only ret1, ... retm, $retPC are left on the stack
- jump
- 0 (1 + n times) to compensate removal of arg1, ..., argn and $retPC
- }
- AssemblyFor('for' { init } condition post body) ->
- {
- init // cannot be its own block because we want variable scope to extend into the body
- // find I such that there are no labels $forI_*
- $forI_begin:
- jumpi($forI_end, iszero(condition))
- { body }
- $forI_continue:
- { post }
- jump($forI_begin)
- $forI_end:
- }
- 'break' ->
- {
- // find nearest enclosing scope with label $forI_end
- pop all local variables that are defined at the current point
- but not at $forI_end
- jump($forI_end)
- 0 (as many as variables were removed above)
- }
- 'continue' ->
- {
- // find nearest enclosing scope with label $forI_continue
- pop all local variables that are defined at the current point
- but not at $forI_continue
- jump($forI_continue)
- 0 (as many as variables were removed above)
- }
- AssemblySwitch(switch condition cases ( default: defaultBlock )? ) ->
- {
- // find I such that there is no $switchI* label or variable
- let $switchI_value := condition
- for each of cases match {
- case val: -> jumpi($switchI_caseJ, eq($switchI_value, val))
- }
- if default block present: ->
- { defaultBlock jump($switchI_end) }
- for each of cases match {
- case val: { body } -> $switchI_caseJ: { body jump($switchI_end) }
- }
- $switchI_end:
- }
- FunctionalAssemblyExpression( identifier(arg1, arg2, ..., argn) ) ->
- {
- if identifier is function <name> with n args and m ret values ->
- {
- // find I such that $funcallI_* does not exist
- $funcallI_return argn ... arg2 arg1 jump(<name>)
- pop (n + 1 times)
- if the current context is `let (id1, ..., idm) := f(...)` ->
- let id1 := 0 ... let idm := 0
- $funcallI_return:
- else ->
- 0 (m times)
- $funcallI_return:
- turn the functional expression that leads to the function call
- into a statement stream
- }
- else -> desugar(children of node)
- }
- default node ->
- desugar(children of node)
- }
-
-Opcode Stream Generation
-------------------------
-
-During opcode stream generation, we keep track of the current stack height
-in a counter,
-so that accessing stack variables by name is possible. The stack height is modified with every opcode
-that modifies the stack and with every label that is annotated with a stack
-adjustment. Every time a new
-local variable is introduced, it is registered together with the current
-stack height. If a variable is accessed (either for copying its value or for
-assignment), the appropriate ``DUP`` or ``SWAP`` instruction is selected depending
-on the difference between the current stack height and the
-stack height at the point the variable was introduced.
-
-Pseudocode::
-
- codegen item: AST -> opcode_stream =
- match item {
- AssemblyBlock({ items }) ->
- join(codegen(item) for item in items)
- if last generated opcode has continuing control flow:
- POP for all local variables registered at the block (including variables
- introduced by labels)
- warn if the stack height at this point is not the same as at the start of the block
- Identifier(id) ->
- lookup id in the syntactic stack of blocks
- match type of id
- Local Variable ->
- DUPi where i = 1 + stack_height - stack_height_of_identifier(id)
- Label ->
- // reference to be resolved during bytecode generation
- PUSH<bytecode position of label>
- SubAssembly ->
- PUSH<bytecode position of subassembly data>
- FunctionalAssemblyExpression(id ( arguments ) ) ->
- join(codegen(arg) for arg in arguments.reversed())
- id (which has to be an opcode, might be a function name later)
- AssemblyLocalDefinition(let (id1, ..., idn) := expr) ->
- register identifiers id1, ..., idn as locals in current block at current stack height
- codegen(expr) - assert that expr returns n items to the stack
- FunctionalAssemblyAssignment((id1, ..., idn) := expr) ->
- lookup id1, ..., idn in the syntactic stack of blocks, assert that they are variables
- codegen(expr)
- for j = n, ..., i:
- SWAPi where i = 1 + stack_height - stack_height_of_identifier(idj)
- POP
- AssemblyAssignment(=: id) ->
- look up id in the syntactic stack of blocks, assert that it is a variable
- SWAPi where i = 1 + stack_height - stack_height_of_identifier(id)
- POP
- LabelDefinition(name:) ->
- JUMPDEST
- NumberLiteral(num) ->
- PUSH<num interpreted as decimal and right-aligned>
- HexLiteral(lit) ->
- PUSH32<lit interpreted as hex and left-aligned>
- StringLiteral(lit) ->
- PUSH32<lit utf-8 encoded and left-aligned>
- SubAssembly(assembly <name> block) ->
- append codegen(block) at the end of the code
- dataSize(<name>) ->
- assert that <name> is a subassembly ->
- PUSH32<size of code generated from subassembly <name>>
- linkerSymbol(<lit>) ->
- PUSH32<zeros> and append position to linker table
- }
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index d96bfde3..2fe1d226 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -432,6 +432,10 @@
"bugs": [],
"released": "2018-04-19"
},
+ "0.4.24": {
+ "bugs": [],
+ "released": "2018-05-16"
+ },
"0.4.3": {
"bugs": [
"ZeroFunctionSelector",
diff --git a/docs/conf.py b/docs/conf.py
index ca8c0fec..7e107f2a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -22,7 +22,8 @@ import re
# documentation root, use os.path.abspath to make it absolute, like shown here.
def setup(sphinx):
- sys.path.insert(0, os.path.abspath('./utils'))
+ thisdir = os.path.dirname(os.path.realpath(__file__))
+ sys.path.insert(0, thisdir + '/utils')
from SolidityLexer import SolidityLexer
sphinx.add_lexer('Solidity', SolidityLexer())
@@ -50,7 +51,7 @@ master_doc = 'index'
# General information about the project.
project = 'Solidity'
-copyright = '2016-2017, Ethereum'
+copyright = '2016-2018, Ethereum'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/docs/contracts.rst b/docs/contracts.rst
index a1f2895c..b73fe2ca 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -24,8 +24,8 @@ Creating contracts programatically on Ethereum is best done via using the JavaSc
As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
to facilitate contract creation.
-When a contract is created, its constructor (a function with the same
-name as the contract) is executed once.
+When a contract is created, its constructor (a function declared with the
+``constructor`` keyword) is executed once.
A constructor is optional. Only one constructor is allowed, and this means
overloading is not supported.
@@ -473,7 +473,7 @@ The following statements are considered modifying the state:
}
.. note::
- ``constant`` on functions is an alias to ``view``, but this is deprecated and is planned to be dropped in version 0.5.0.
+ ``constant`` on functions is an alias to ``view``, but this is deprecated and will be dropped in version 0.5.0.
.. note::
Getter methods are marked ``view``.
@@ -841,10 +841,10 @@ Details are given in the following example.
::
- pragma solidity ^0.4.16;
+ pragma solidity ^0.4.22;
contract owned {
- function owned() { owner = msg.sender; }
+ constructor() { owner = msg.sender; }
address owner;
}
@@ -875,7 +875,7 @@ Details are given in the following example.
// also a base class of `mortal`, yet there is only a single
// instance of `owned` (as for virtual inheritance in C++).
contract named is owned, mortal {
- function named(bytes32 name) {
+ constructor(bytes32 name) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name);
}
@@ -913,10 +913,10 @@ Note that above, we call ``mortal.kill()`` to "forward" the
destruction request. The way this is done is problematic, as
seen in the following example::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.22;
contract owned {
- function owned() public { owner = msg.sender; }
+ constructor() public { owner = msg.sender; }
address owner;
}
@@ -942,10 +942,10 @@ derived override, but this function will bypass
``Base1.kill``, basically because it does not even know about
``Base1``. The way around this is to use ``super``::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.22;
contract owned {
- function owned() public { owner = msg.sender; }
+ constructor() public { owner = msg.sender; }
address owner;
}
@@ -982,7 +982,7 @@ virtual method lookup.
Constructors
============
-A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
+A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the
default constructor: ``contructor() public {}``.
@@ -1030,10 +1030,11 @@ A constructor set as ``internal`` causes the contract to be marked as :ref:`abst
Arguments for Base Constructors
===============================
-Derived contracts need to provide all arguments needed for
-the base constructors. This can be done in two ways::
+The constructors of all the base contracts will be called following the
+linearization rules explained below. If the base constructors have arguments,
+derived contracts need to specify all of them. This can be done in two ways::
- pragma solidity ^0.4.0;
+ pragma solidity ^0.4.22;
contract Base {
uint x;
@@ -1059,6 +1060,9 @@ derived contract. Arguments have to be given either in the
inheritance list or in modifier-style in the derived constuctor.
Specifying arguments in both places is an error.
+If a derived contract doesn't specify the arguments to all of its base
+contracts' constructors, it will be abstract.
+
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
Multiple Inheritance and Linearization
@@ -1066,12 +1070,15 @@ Multiple Inheritance and Linearization
Languages that allow multiple inheritance have to deal with
several problems. One is the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_.
-Solidity follows the path of Python and uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_"
+Solidity is similar to Python in that it uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_"
to force a specific order in the DAG of base classes. This
results in the desirable property of monotonicity but
disallows some inheritance graphs. Especially, the order in
which the base classes are given in the ``is`` directive is
-important. In the following code, Solidity will give the
+important: You have to list the direct base contracts
+in the order from "most base-like" to "most derived".
+Note that this order is different from the one used in Python.
+In the following code, Solidity will give the
error "Linearization of inheritance graph impossible".
::
@@ -1089,9 +1096,6 @@ The reason for this is that ``C`` requests ``X`` to override ``A``
requests to override ``X``, which is a contradiction that
cannot be resolved.
-A simple rule to remember is to specify the base classes in
-the order from "most base-like" to "most derived".
-
Inheriting Different Kinds of Members of the Same Name
======================================================
@@ -1139,8 +1143,10 @@ Example of a Function Type (a variable declaration, where the variable is of typ
function(address) external returns (address) foo;
-Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
+Abstract contracts decouple the definition of a contract from its implementation providing better extensibility and self-documentation and
facilitating patterns like the `Template method <https://en.wikipedia.org/wiki/Template_method_pattern>`_ and removing code duplication.
+Abstract contracts are useful in the same way that defining methods in an interface is useful. It is a way for the designer of the abstract contract to say "any child of mine must implement this method".
+
.. index:: ! contract;interface, ! interface contract
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index f3c351dd..7849d15a 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -272,9 +272,12 @@ Assignment
Destructuring Assignments and Returning Multiple Values
-------------------------------------------------------
-Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time::
+Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time.
+These can then either be assigned to newly declared variables or to pre-existing variables (or LValues in general):
- pragma solidity ^0.4.16;
+::
+
+ pragma solidity >0.4.23 <0.5.0;
contract C {
uint[] data;
@@ -284,21 +287,12 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di
}
function g() public {
- // Variables declared with type
- uint x;
- bool b;
- uint y;
- // Tuple values can be assigned to these pre-existing variables
- (x, b, y) = f();
+ // Variables declared with type and assigned from the returned tuple.
+ (uint x, bool b, uint y) = f();
// Common trick to swap values -- does not work for non-value storage types.
(x, y) = (y, x);
// Components can be left out (also for variable declarations).
- // If the tuple ends in an empty component,
- // the rest of the values are discarded.
- (data.length,) = f(); // Sets the length to 7
- // The same can be done on the left side.
- // If the tuple begins in an empty component, the beginning values are discarded.
- (,data[3]) = f(); // Sets data[3] to 2
+ (data.length,,) = f(); // Sets the length to 7
// Components can only be left out at the left-hand-side of assignments, with
// one exception:
(x,) = (1,);
@@ -307,6 +301,11 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di
}
}
+.. note::
+ Prior to version 0.4.24 it was possible to assign to tuples of smaller size, either
+ filling up on the left or on the right side (which ever was empty). This is
+ now deprecated, both sides have to have the same number of components.
+
Complications for Arrays and Structs
------------------------------------
@@ -330,7 +329,9 @@ A variable declared anywhere within a function will be in scope for the *entire
(this will change soon, see below).
This happens because Solidity inherits its scoping rules from JavaScript.
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
-As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
+As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``:
+
+::
// This will not compile
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 6a2fe685..ca5a1aee 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -203,7 +203,7 @@ situation.
If you do not want to throw, you can return a pair::
- pragma solidity ^0.4.16;
+ pragma solidity >0.4.23 <0.5.0;
contract C {
uint[] counters;
@@ -219,7 +219,7 @@ If you do not want to throw, you can return a pair::
}
function checkCounter(uint index) public view {
- var (counter, error) = getCounter(index);
+ (uint counter, bool error) = getCounter(index);
if (error) {
// ...
} else {
diff --git a/docs/grammar.txt b/docs/grammar.txt
index b4ca5ca9..0dda4f49 100644
--- a/docs/grammar.txt
+++ b/docs/grammar.txt
@@ -16,7 +16,7 @@ ContractPart = StateVariableDeclaration | UsingForDeclaration
InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )?
-StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )? Identifier ('=' Expression)? ';'
+StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )* Identifier ('=' Expression)? ';'
UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';'
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration ';' (VariableDeclaration ';')* ) '}'
@@ -78,7 +78,7 @@ Break = 'break'
Return = 'return' Expression?
Throw = 'throw'
EmitStatement = 'emit' FunctionCall
-VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )?
+VariableDefinition = ('var' IdentifierList | VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )?
IdentifierList = '(' ( Identifier? ',' )* Identifier? ')'
// Precedence by order (see github.com/ethereum/solidity/pull/732)
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index 6726ded9..05ee0748 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -35,7 +35,7 @@ npm / Node.js
=============
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The
-`solcjs` program has less features than all options further down this page. Our
+`solcjs` program has fewer features than all options further down this page. Our
:ref:`commandline-compiler` documentation assumes you are using
the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will
stop reading the documentation here and then continue to `solc-js <https://github.com/ethereum/solc-js>`_.
@@ -203,19 +203,38 @@ Prerequisites - Windows
You will need to install the following dependencies for Windows builds of Solidity:
-+------------------------------+-------------------------------------------------------+
-| Software | Notes |
-+==============================+=======================================================+
-| `Git for Windows`_ | Command-line tool for retrieving source from Github. |
-+------------------------------+-------------------------------------------------------+
-| `CMake`_ | Cross-platform build file generator. |
-+------------------------------+-------------------------------------------------------+
-| `Visual Studio 2015`_ | C++ compiler and dev environment. |
-+------------------------------+-------------------------------------------------------+
++-----------------------------------+-------------------------------------------------------+
+| Software | Notes |
++===================================+=======================================================+
+| `Git for Windows`_ | Command-line tool for retrieving source from Github. |
++-----------------------------------+-------------------------------------------------------+
+| `CMake`_ | Cross-platform build file generator. |
++-----------------------------------+-------------------------------------------------------+
+| `Visual Studio 2017 Build Tools`_ | C++ compiler |
++-----------------------------------+-------------------------------------------------------+
+| `Visual Studio 2017`_ (Optional) | C++ compiler and dev environment. |
++-----------------------------------+-------------------------------------------------------+
+
+If you've already had one IDE and only need compiler and libraries,
+you could install Visual Studio 2017 Build Tools.
+
+Visual Studio 2017 provides both IDE and necessary compiler and libraries.
+So if you have not got an IDE and prefer to develop solidity, Visual Studio 2017
+may be an choice for you to get everything setup easily.
+
+Here is the list of components that should be installed
+in Visual Studio 2017 Build Tools or Visual Studio 2017:
+
+* Visual Studio C++ core features
+* VC++ 2017 v141 toolset (x86,x64)
+* Windows Universal CRT SDK
+* Windows 8.1 SDK
+* C++/CLI support
.. _Git for Windows: https://git-scm.com/download/win
.. _CMake: https://cmake.org/download/
-.. _Visual Studio 2015: https://www.visualstudio.com/products/vs-2015-product-editions
+.. _Visual Studio 2017: https://www.visualstudio.com/vs/
+.. _Visual Studio 2017 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017
External Dependencies
@@ -263,7 +282,7 @@ And even for Windows:
mkdir build
cd build
- cmake -G "Visual Studio 14 2015 Win64" ..
+ cmake -G "Visual Studio 15 2017 Win64" ..
This latter set of instructions should result in the creation of
**solidity.sln** in that build directory. Double-clicking on that file
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index 84b1fff8..d1789c44 100644
--- a/docs/introduction-to-smart-contracts.rst
+++ b/docs/introduction-to-smart-contracts.rst
@@ -25,7 +25,7 @@ Storage
storedData = x;
}
- function get() public constant returns (uint) {
+ function get() public view returns (uint) {
return storedData;
}
}
diff --git a/docs/julia.rst b/docs/julia.rst
index 078bc55b..c9b73db2 100644
--- a/docs/julia.rst
+++ b/docs/julia.rst
@@ -306,12 +306,20 @@ Type Conversion Functions
JULIA has no support for implicit type conversion and therefore functions exists to provide explicit conversion.
When converting a larger type to a shorter type a runtime exception can occur in case of an overflow.
-The following type conversion functions must be available:
-- ``u32tobool(x:u32) -> y:bool``
-- ``booltou32(x:bool) -> y:u32``
-- ``u32tou64(x:u32) -> y:u64``
-- ``u64tou32(x:u64) -> y:u32``
-- etc. (TBD)
+Truncating conversions are supported between the following types:
+ - ``bool``
+ - ``u32``
+ - ``u64``
+ - ``u256``
+ - ``s256``
+
+For each of these a type conversion function exists having the prototype in the form of ``<input_type>to<output_type>(x:<input_type>) -> y:<output_type>``,
+such as ``u32tobool(x:u32) -> y:bool``, ``u256tou32(x:u256) -> y:u32`` or ``s256tou256(x:s256) -> y:u256``.
+
+.. note::
+
+ ``u32tobool(x:u32) -> y:bool`` can be implemented as ``y := not(iszerou256(x))`` and
+ ``booltou32(x:bool) -> y:u32`` can be implemented as ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }``
Low-level Functions
-------------------
@@ -319,6 +327,16 @@ Low-level Functions
The following functions must be available:
+---------------------------------------------------------------------------------------------------------------+
+| *Logic* |
++---------------------------------------------+-----------------------------------------------------------------+
+| not(x:bool) -> z:bool | logical not |
++---------------------------------------------+-----------------------------------------------------------------+
+| and(x:bool, y:bool) -> z:bool | logical and |
++---------------------------------------------+-----------------------------------------------------------------+
+| or(x:bool, y:bool) -> z:bool | logical or |
++---------------------------------------------+-----------------------------------------------------------------+
+| xor(x:bool, y:bool) -> z:bool | xor |
++---------------------------------------------+-----------------------------------------------------------------+
| *Arithmetics* |
+---------------------------------------------+-----------------------------------------------------------------+
| addu256(x:u256, y:u256) -> z:u256 | x + y |
@@ -343,15 +361,19 @@ The following functions must be available:
+---------------------------------------------+-----------------------------------------------------------------+
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics |
+---------------------------------------------+-----------------------------------------------------------------+
-| ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise |
+| ltu256(x:u256, y:u256) -> z:bool | true if x < y, false otherwise |
+---------------------------------------------+-----------------------------------------------------------------+
-| gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise |
+| gtu256(x:u256, y:u256) -> z:bool | true if x > y, false otherwise |
+---------------------------------------------+-----------------------------------------------------------------+
-| sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
+| sltu256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise |
+| | (for signed numbers in two's complement) |
+---------------------------------------------+-----------------------------------------------------------------+
-| sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
+| sgtu256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise |
+| | (for signed numbers in two's complement) |
+---------------------------------------------+-----------------------------------------------------------------+
-| equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise |
+| equ256(x:u256, y:u256) -> z:bool | true if x == y, false otherwise |
++---------------------------------------------+-----------------------------------------------------------------+
+| iszerou256(x:u256) -> z:bool | true if x == 0, false otherwise |
+---------------------------------------------+-----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, every bit of x is negated |
+---------------------------------------------+-----------------------------------------------------------------+
@@ -405,10 +427,6 @@ The following functions must be available:
| insize:u256, out:u256, | but also keep ``caller`` |
| outsize:u256) -> r:u256 | and ``callvalue`` |
+---------------------------------------------+-----------------------------------------------------------------+
-| stop() | stop execution, identical to return(0,0) |
-| | Perhaps it would make sense retiring this as it equals to |
-| | return(0,0). It can be an optimisation by the EVM backend. |
-+---------------------------------------------+-----------------------------------------------------------------+
| abort() | abort (equals to invalid instruction on EVM) |
+---------------------------------------------+-----------------------------------------------------------------+
| return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) |
@@ -473,15 +491,17 @@ The following functions must be available:
+---------------------------------------------+-----------------------------------------------------------------+
| *Others* |
+---------------------------------------------+-----------------------------------------------------------------+
+| discard(unused:bool) | discard value |
++---------------------------------------------+-----------------------------------------------------------------+
| discardu256(unused:u256) | discard value |
+---------------------------------------------+-----------------------------------------------------------------+
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's |
-| x3:u64, x4:u64) | |
+| x3:u64, x4:u64) | |
+---------------------------------------------+-----------------------------------------------------------------+
| combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 |
-| x4:u64) -> (x:u256) | |
+| x4:u64) -> (x:u256) | |
+---------------------------------------------+-----------------------------------------------------------------+
-| sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) |
+| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) |
+---------------------------------------------+-----------------------------------------------------------------+
Backends
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 00000000..0607b1ef
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1 @@
+sphinx_rtd_theme>=0.3.1
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 3e1c3a12..4133edb1 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -120,7 +120,7 @@ Gas Limit and Loops
Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully:
Due to the block gas limit, transactions can only consume a certain amount of gas. Either explicitly or just due to
normal operation, the number of iterations in a loop can grow beyond the block gas limit which can cause the complete
-contract to be stalled at a certain point. This may not apply to ``constant`` functions that are only executed
+contract to be stalled at a certain point. This may not apply to ``view`` functions that are only executed
to read data from the blockchain. Still, such functions may be called by other contracts as part of on-chain operations
and stall those. Please be explicit about such cases in the documentation of your contracts.
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index 546767e4..2b3d4b48 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -66,7 +66,7 @@ of votes.
Proposal[] public proposals;
/// Create a new ballot to choose one of `proposalNames`.
- function Ballot(bytes32[] proposalNames) public {
+ constructor(bytes32[] proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
@@ -256,7 +256,7 @@ activate themselves.
/// Create a simple auction with `_biddingTime`
/// seconds bidding time on behalf of the
/// beneficiary address `_beneficiary`.
- function SimpleAuction(
+ constructor(
uint _biddingTime,
address _beneficiary
) public {
@@ -388,7 +388,7 @@ high or low invalid bids.
::
- pragma solidity ^0.4.22;
+ pragma solidity >0.4.23 <0.5.0;
contract BlindAuction {
struct Bid {
@@ -418,7 +418,7 @@ high or low invalid bids.
modifier onlyBefore(uint _time) { require(now < _time); _; }
modifier onlyAfter(uint _time) { require(now > _time); _; }
- function BlindAuction(
+ constructor(
uint _biddingTime,
uint _revealTime,
address _beneficiary
@@ -467,8 +467,8 @@ high or low invalid bids.
uint refund;
for (uint i = 0; i < length; i++) {
- var bid = bids[msg.sender][i];
- var (value, fake, secret) =
+ Bid storage bid = bids[msg.sender][i];
+ (uint value, bool fake, bytes32 secret) =
(_values[i], _fake[i], _secret[i]);
if (bid.blindedBid != keccak256(value, fake, secret)) {
// Bid was not actually revealed.
@@ -553,7 +553,7 @@ Safe Remote Purchase
// Ensure that `msg.value` is an even number.
// Division will truncate if it is an odd number.
// Check via multiplication that it wasn't an odd number.
- function Purchase() public payable {
+ constructor() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value, "Value has to be even.");
@@ -602,7 +602,7 @@ Safe Remote Purchase
{
emit Aborted();
state = State.Inactive;
- seller.transfer(this.balance);
+ seller.transfer(address(this).balance);
}
/// Confirm the purchase as buyer.
@@ -637,7 +637,7 @@ Safe Remote Purchase
// block the refund - the withdraw pattern should be used.
buyer.transfer(value);
- seller.transfer(this.balance);
+ seller.transfer(address(this).balance);
}
}
diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst
index d57f1703..7a6317eb 100644
--- a/docs/structure-of-a-contract.rst
+++ b/docs/structure-of-a-contract.rst
@@ -129,7 +129,7 @@ Structs are custom defined types that can group several variables (see
Enum Types
==========
-Enums can be used to create custom types with a finite set of values (see
+Enums can be used to create custom types with a finite set of 'constant values' (see
:ref:`enums` in types section).
::
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
index ee1ea4bd..6b28f2ab 100644
--- a/docs/style-guide.rst
+++ b/docs/style-guide.rst
@@ -117,7 +117,7 @@ No::
Maximum Line Length
===================
-Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ of 79 (or 99)
+Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ to a maximum of 79 (or 99)
characters helps readers easily parse the code.
Wrapped lines should conform to the following guidelines.
@@ -269,7 +269,7 @@ Functions should be grouped according to their visibility and ordered:
- internal
- private
-Within a grouping, place the ``constant`` functions last.
+Within a grouping, place the ``view`` and ``pure`` functions last.
Yes::
@@ -285,7 +285,10 @@ Yes::
// External functions
// ...
- // External functions that are constant
+ // External functions that are view
+ // ...
+
+ // External functions that are pure
// ...
// Public functions
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index 51f7b9f3..a6f8ca87 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -31,6 +31,9 @@ because of `leap seconds <https://en.wikipedia.org/wiki/Leap_second>`_.
Due to the fact that leap seconds cannot be predicted, an exact calendar
library has to be updated by an external oracle.
+.. note::
+ The suffix ``years`` has been deprecated due to the reasons above.
+
These suffixes cannot be applied to variables. If you want to
interpret some input variable in e.g. days, you can do it in the following way::
@@ -117,11 +120,11 @@ Error Handling
--------------
``assert(bool condition)``:
- throws if the condition is not met - to be used for internal errors.
+ invalidates the transaction if the condition is not met - to be used for internal errors.
``require(bool condition)``:
- throws if the condition is not met - to be used for errors in inputs or external components.
+ reverts if the condition is not met - to be used for errors in inputs or external components.
``require(bool condition, string message)``:
- throws if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.
+ reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.
``revert()``:
abort execution and revert state changes
``revert(string reason)``:
diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst
index df30b6b4..1d7cb97b 100644
--- a/docs/using-the-compiler.rst
+++ b/docs/using-the-compiler.rst
@@ -14,7 +14,9 @@ Using the Commandline Compiler
One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler.
Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
-If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``.
+If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``.
+
+Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. By default, the optimizer will optimize the contract for 200 runs. If you want to optimize for initial contract deployment and get the smallest output, set it to ``--runs=1``. If you expect many transactions and don't care for higher deployment cost and output size, set ``--runs`` to a high number.
The commandline compiler will automatically read imported files from the filesystem, but
it is also possible to provide path redirects using ``prefix=path`` in the following way:
@@ -96,10 +98,13 @@ Input Description
{
// Optional: Sorted list of remappings
remappings: [ ":g/dir" ],
- // Optional: Optimizer settings (enabled defaults to false)
+ // Optional: Optimizer settings
optimizer: {
+ // disabled by default
enabled: true,
- runs: 500
+ // Optimize for how many times you intend to run the code.
+ // Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage.
+ runs: 200
},
evmVersion: "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
// Metadata settings (optional)
diff --git a/libdevcore/JSON.cpp b/libdevcore/JSON.cpp
index d99b3bc6..6317cc89 100644
--- a/libdevcore/JSON.cpp
+++ b/libdevcore/JSON.cpp
@@ -28,8 +28,8 @@
using namespace std;
static_assert(
- (JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 7) && (JSONCPP_VERSION_PATCH == 7),
- "Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.7.7."
+ (JSONCPP_VERSION_MAJOR == 1) && (JSONCPP_VERSION_MINOR == 8) && (JSONCPP_VERSION_PATCH == 4),
+ "Unexpected jsoncpp version: " JSONCPP_VERSION_STRING ". Expecting 1.8.4."
);
namespace dev
diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h
index 367c6daa..1ed9b859 100644
--- a/libevmasm/Assembly.h
+++ b/libevmasm/Assembly.h
@@ -47,8 +47,8 @@ class Assembly
public:
Assembly() {}
- AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
- AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
+ AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
+ AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
AssemblyItem namedTag(std::string const& _name);
AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp
index 5af618ff..a3a4d6b6 100644
--- a/libevmasm/AssemblyItem.cpp
+++ b/libevmasm/AssemblyItem.cpp
@@ -26,27 +26,35 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
+static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide");
+
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
{
assertThrow(data() < (u256(1) << 64), Exception, "Tag already has subassembly set.");
-
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
+ size_t tag = size_t(u256(data()) & 0xffffffffffffffffULL);
AssemblyItem r = *this;
r.m_type = PushTag;
- r.setPushTagSubIdAndTag(_subId, size_t(data()));
+ r.setPushTagSubIdAndTag(_subId, tag);
return r;
}
pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
{
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
- return make_pair(size_t((data()) / (u256(1) << 64)) - 1, size_t(data()));
+ u256 combined = u256(data());
+ size_t subId = size_t((combined >> 64) - 1);
+ size_t tag = size_t(combined & 0xffffffffffffffffULL);
+ return make_pair(subId, tag);
}
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
{
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
- setData(_tag + (u256(_subId + 1) << 64));
+ u256 data = _tag;
+ if (_subId != size_t(-1))
+ data |= (u256(_subId) + 1) << 64;
+ setData(data);
}
unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt
index cfd59dff..86192c1b 100644
--- a/libevmasm/CMakeLists.txt
+++ b/libevmasm/CMakeLists.txt
@@ -2,4 +2,4 @@ file(GLOB sources "*.cpp")
file(GLOB headers "*.h")
add_library(evmasm ${sources} ${headers})
-target_link_libraries(evmasm PUBLIC jsoncpp devcore)
+target_link_libraries(evmasm PUBLIC devcore)
diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h
index 9b60b26b..f0deb387 100644
--- a/libevmasm/ConstantOptimiser.h
+++ b/libevmasm/ConstantOptimiser.h
@@ -67,6 +67,7 @@ public:
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value):
m_params(_params), m_value(_value) {}
+ virtual ~ConstantOptimisationMethod() = default;
virtual bigint gasNeeded() const = 0;
/// Executes the method, potentially appending to the assembly and returns a vector of
/// assembly items the constant should be relpaced with in one sweep.
diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h
index be788ddb..dc116f88 100644
--- a/libevmasm/Instruction.h
+++ b/libevmasm/Instruction.h
@@ -39,7 +39,7 @@ enum class Instruction: uint8_t
{
STOP = 0x00, ///< halts execution
ADD, ///< addition operation
- MUL, ///< mulitplication operation
+ MUL, ///< multiplication operation
SUB, ///< subtraction operation
DIV, ///< integer division operation
SDIV, ///< signed integer division operation
@@ -50,11 +50,11 @@ enum class Instruction: uint8_t
EXP, ///< exponential operation
SIGNEXTEND, ///< extend length of signed integer
- LT = 0x10, ///< less-than comparision
- GT, ///< greater-than comparision
- SLT, ///< signed less-than comparision
- SGT, ///< signed greater-than comparision
- EQ, ///< equality comparision
+ LT = 0x10, ///< less-than comparison
+ GT, ///< greater-than comparison
+ SLT, ///< signed less-than comparison
+ SGT, ///< signed greater-than comparison
+ EQ, ///< equality comparison
ISZERO, ///< simple not operator
AND, ///< bitwise AND operation
OR, ///< bitwise OR operation
@@ -293,7 +293,7 @@ struct InstructionInfo
/// Information on all the instructions.
InstructionInfo instructionInfo(Instruction _inst);
-/// check whether instructions exists
+/// check whether instructions exists.
bool isValidInstruction(Instruction _inst);
/// Convert from string mnemonic to Instruction type.
diff --git a/libevmasm/PathGasMeter.cpp b/libevmasm/PathGasMeter.cpp
index 3fe682b7..cdadba76 100644
--- a/libevmasm/PathGasMeter.cpp
+++ b/libevmasm/PathGasMeter.cpp
@@ -43,7 +43,7 @@ GasMeter::GasConsumption PathGasMeter::estimateMax(
auto path = unique_ptr<GasPath>(new GasPath());
path->index = _startIndex;
path->state = _state->copy();
- m_queue.push_back(move(path));
+ queue(move(path));
GasMeter::GasConsumption gas;
while (!m_queue.empty() && !gas.isInfinite)
@@ -51,12 +51,23 @@ GasMeter::GasConsumption PathGasMeter::estimateMax(
return gas;
}
+void PathGasMeter::queue(std::unique_ptr<GasPath>&& _newPath)
+{
+ if (
+ m_highestGasUsagePerJumpdest.count(_newPath->index) &&
+ _newPath->gas < m_highestGasUsagePerJumpdest.at(_newPath->index)
+ )
+ return;
+ m_highestGasUsagePerJumpdest[_newPath->index] = _newPath->gas;
+ m_queue[_newPath->index] = move(_newPath);
+}
+
GasMeter::GasConsumption PathGasMeter::handleQueueItem()
{
assertThrow(!m_queue.empty(), OptimizerException, "");
- unique_ptr<GasPath> path = move(m_queue.back());
- m_queue.pop_back();
+ unique_ptr<GasPath> path = move(m_queue.rbegin()->second);
+ m_queue.erase(--m_queue.end());
shared_ptr<KnownState> state = path->state;
GasMeter meter(state, m_evmVersion, path->largestMemoryAccess);
@@ -117,7 +128,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
newPath->largestMemoryAccess = meter.largestMemoryAccess();
newPath->state = state->copy();
newPath->visitedJumpdests = path->visitedJumpdests;
- m_queue.push_back(move(newPath));
+ queue(move(newPath));
}
if (branchStops)
diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h
index 2527d7fb..9537b176 100644
--- a/libevmasm/PathGasMeter.h
+++ b/libevmasm/PathGasMeter.h
@@ -58,9 +58,17 @@ public:
GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state);
private:
+ /// Adds a new path item to the queue, but only if we do not already have
+ /// a higher gas usage at that point.
+ /// This is not exact as different state might influence higher gas costs at a later
+ /// point in time, but it greatly reduces computational overhead.
+ void queue(std::unique_ptr<GasPath>&& _newPath);
GasMeter::GasConsumption handleQueueItem();
- std::vector<std::unique_ptr<GasPath>> m_queue;
+ /// Map of jumpdest -> gas path, so not really a queue. We only have one queued up
+ /// item per jumpdest, because of the behaviour of `queue` above.
+ std::map<size_t, std::unique_ptr<GasPath>> m_queue;
+ std::map<size_t, GasMeter::GasConsumption> m_highestGasUsagePerJumpdest;
std::map<u256, size_t> m_tagPositions;
AssemblyItems const& m_items;
solidity::EVMVersion m_evmVersion;
diff --git a/libevmasm/PeepholeOptimiser.h b/libevmasm/PeepholeOptimiser.h
index a74cc8b3..a651143d 100644
--- a/libevmasm/PeepholeOptimiser.h
+++ b/libevmasm/PeepholeOptimiser.h
@@ -34,6 +34,7 @@ using AssemblyItems = std::vector<AssemblyItem>;
class PeepholeOptimisationMethod
{
public:
+ virtual ~PeepholeOptimisationMethod() = default;
virtual size_t windowSize() const;
virtual bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out);
};
@@ -42,6 +43,7 @@ class PeepholeOptimiser
{
public:
explicit PeepholeOptimiser(AssemblyItems& _items): m_items(_items) {}
+ virtual ~PeepholeOptimiser() = default;
bool optimise();
diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h
index abcf170c..2b7da01b 100644
--- a/libevmasm/RuleList.h
+++ b/libevmasm/RuleList.h
@@ -174,6 +174,26 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
});
}
+ for (auto const& op: std::vector<Instruction>{
+ Instruction::ADDRESS,
+ Instruction::CALLER,
+ Instruction::ORIGIN,
+ Instruction::COINBASE
+ })
+ {
+ u256 const mask = (u256(1) << 160) - 1;
+ rules.push_back({
+ {Instruction::AND, {{op, mask}}},
+ [=]() -> Pattern { return op; },
+ false
+ });
+ rules.push_back({
+ {Instruction::AND, {{mask, op}}},
+ [=]() -> Pattern { return op; },
+ false
+ });
+ }
+
// Double negation of opcodes with boolean result
for (auto const& op: std::vector<Instruction>{
Instruction::EQ,
diff --git a/libjulia/Exceptions.h b/libjulia/Exceptions.h
new file mode 100644
index 00000000..20ab6520
--- /dev/null
+++ b/libjulia/Exceptions.h
@@ -0,0 +1,35 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Exceptions in Julia.
+ */
+
+#pragma once
+
+#include <libdevcore/Exceptions.h>
+#include <libdevcore/Assertions.h>
+
+namespace dev
+{
+namespace julia
+{
+
+struct IuliaException: virtual Exception {};
+struct OptimizerException: virtual IuliaException {};
+
+}
+}
diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp
index 5c47be64..a8a1e30f 100644
--- a/libjulia/optimiser/ASTCopier.cpp
+++ b/libjulia/optimiser/ASTCopier.cpp
@@ -20,9 +20,9 @@
#include <libjulia/optimiser/ASTCopier.h>
-#include <libsolidity/inlineasm/AsmData.h>
+#include <libjulia/Exceptions.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmData.h>
#include <libdevcore/Common.h>
@@ -30,10 +30,9 @@ using namespace std;
using namespace dev;
using namespace dev::julia;
-
Statement ASTCopier::operator()(Instruction const&)
{
- solAssert(false, "Invalid operation.");
+ assertThrow(false, OptimizerException, "Invalid operation.");
return {};
}
@@ -62,13 +61,13 @@ Statement ASTCopier::operator()(Assignment const& _assignment)
Statement ASTCopier::operator()(StackAssignment const&)
{
- solAssert(false, "Invalid operation.");
+ assertThrow(false, OptimizerException, "Invalid operation.");
return {};
}
Statement ASTCopier::operator()(Label const&)
{
- solAssert(false, "Invalid operation.");
+ assertThrow(false, OptimizerException, "Invalid operation.");
return {};
}
diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h
index 36a1ced5..8681f2e0 100644
--- a/libjulia/optimiser/ASTCopier.h
+++ b/libjulia/optimiser/ASTCopier.h
@@ -37,6 +37,7 @@ namespace julia
class ExpressionCopier: public boost::static_visitor<Expression>
{
public:
+ virtual ~ExpressionCopier() = default;
virtual Expression operator()(Literal const& _literal) = 0;
virtual Expression operator()(Identifier const& _identifier) = 0;
virtual Expression operator()(FunctionalInstruction const& _instr) = 0;
@@ -46,6 +47,7 @@ public:
class StatementCopier: public boost::static_visitor<Statement>
{
public:
+ virtual ~StatementCopier() = default;
virtual Statement operator()(ExpressionStatement const& _statement) = 0;
virtual Statement operator()(Instruction const& _instruction) = 0;
virtual Statement operator()(Label const& _label) = 0;
@@ -66,6 +68,7 @@ public:
class ASTCopier: public ExpressionCopier, public StatementCopier
{
public:
+ virtual ~ASTCopier() = default;
virtual Expression operator()(Literal const& _literal) override;
virtual Statement operator()(Instruction const& _instruction) override;
virtual Expression operator()(Identifier const& _identifier) override;
diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp
index 03444984..dc94cc60 100644
--- a/libjulia/optimiser/ASTWalker.cpp
+++ b/libjulia/optimiser/ASTWalker.cpp
@@ -22,8 +22,6 @@
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Exceptions.h>
-
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h
index 97891381..e1f0f5bd 100644
--- a/libjulia/optimiser/ASTWalker.h
+++ b/libjulia/optimiser/ASTWalker.h
@@ -22,7 +22,7 @@
#include <libjulia/ASTDataForward.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <libjulia/Exceptions.h>
#include <boost/variant.hpp>
#include <boost/optional.hpp>
@@ -42,14 +42,15 @@ namespace julia
class ASTWalker: public boost::static_visitor<>
{
public:
+ virtual ~ASTWalker() = default;
virtual void operator()(Literal const&) {}
- virtual void operator()(Instruction const&) { solAssert(false, ""); }
+ virtual void operator()(Instruction const&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(Identifier const&) {}
virtual void operator()(FunctionalInstruction const& _instr);
virtual void operator()(FunctionCall const& _funCall);
virtual void operator()(ExpressionStatement const& _statement);
- virtual void operator()(Label const&) { solAssert(false, ""); }
- virtual void operator()(StackAssignment const&) { solAssert(false, ""); }
+ virtual void operator()(Label const&) { assertThrow(false, OptimizerException, ""); }
+ virtual void operator()(StackAssignment const&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(Assignment const& _assignment);
virtual void operator()(VariableDeclaration const& _varDecl);
virtual void operator()(If const& _if);
@@ -82,14 +83,15 @@ protected:
class ASTModifier: public boost::static_visitor<>
{
public:
+ virtual ~ASTModifier() = default;
virtual void operator()(Literal&) {}
- virtual void operator()(Instruction&) { solAssert(false, ""); }
+ virtual void operator()(Instruction&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(Identifier&) {}
virtual void operator()(FunctionalInstruction& _instr);
virtual void operator()(FunctionCall& _funCall);
virtual void operator()(ExpressionStatement& _statement);
- virtual void operator()(Label&) { solAssert(false, ""); }
- virtual void operator()(StackAssignment&) { solAssert(false, ""); }
+ virtual void operator()(Label&) { assertThrow(false, OptimizerException, ""); }
+ virtual void operator()(StackAssignment&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(Assignment& _assignment);
virtual void operator()(VariableDeclaration& _varDecl);
virtual void operator()(If& _if);
diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.cpp b/libjulia/optimiser/CommonSubexpressionEliminator.cpp
index 229bd35e..3122280b 100644
--- a/libjulia/optimiser/CommonSubexpressionEliminator.cpp
+++ b/libjulia/optimiser/CommonSubexpressionEliminator.cpp
@@ -23,6 +23,7 @@
#include <libjulia/optimiser/Metrics.h>
#include <libjulia/optimiser/SyntacticalEquality.h>
+#include <libjulia/Exceptions.h>
#include <libsolidity/inlineasm/AsmData.h>
@@ -37,7 +38,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
// TODO this search rather inefficient.
for (auto const& var: m_value)
{
- solAssert(var.second, "");
+ assertThrow(var.second, OptimizerException, "");
if (SyntacticalEqualityChecker::equal(_e, *var.second))
{
_e = Identifier{locationOf(_e), var.first};
diff --git a/libjulia/optimiser/DataFlowAnalyzer.cpp b/libjulia/optimiser/DataFlowAnalyzer.cpp
index 56653393..25f0ffb4 100644
--- a/libjulia/optimiser/DataFlowAnalyzer.cpp
+++ b/libjulia/optimiser/DataFlowAnalyzer.cpp
@@ -23,11 +23,11 @@
#include <libjulia/optimiser/DataFlowAnalyzer.h>
#include <libjulia/optimiser/NameCollector.h>
+#include <libjulia/optimiser/Semantics.h>
+#include <libjulia/Exceptions.h>
#include <libsolidity/inlineasm/AsmData.h>
-#include <libjulia/optimiser/Semantics.h>
-
#include <libdevcore/CommonData.h>
#include <boost/range/adaptor/reversed.hpp>
@@ -41,7 +41,7 @@ void DataFlowAnalyzer::operator()(Assignment& _assignment)
set<string> names;
for (auto const& var: _assignment.variableNames)
names.insert(var.name);
- solAssert(_assignment.value, "");
+ assertThrow(_assignment.value, OptimizerException, "");
visit(*_assignment.value);
handleAssignment(names, _assignment.value.get());
}
@@ -120,7 +120,7 @@ void DataFlowAnalyzer::operator()(Block& _block)
m_variableScopes.emplace_back(false);
ASTModifier::operator()(_block);
m_variableScopes.pop_back();
- solAssert(numScopes == m_variableScopes.size(), "");
+ assertThrow(numScopes == m_variableScopes.size(), OptimizerException, "");
}
void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expression* _value)
diff --git a/libjulia/optimiser/Disambiguator.cpp b/libjulia/optimiser/Disambiguator.cpp
index df2984e5..687be9b9 100644
--- a/libjulia/optimiser/Disambiguator.cpp
+++ b/libjulia/optimiser/Disambiguator.cpp
@@ -20,11 +20,11 @@
#include <libjulia/optimiser/Disambiguator.h>
+#include <libjulia/Exceptions.h>
+
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/inlineasm/AsmScope.h>
-#include <libsolidity/interface/Exceptions.h>
-
using namespace std;
using namespace dev;
using namespace dev::julia;
@@ -34,21 +34,11 @@ using Scope = dev::solidity::assembly::Scope;
string Disambiguator::translateIdentifier(string const& _originalName)
{
- solAssert(!m_scopes.empty() && m_scopes.back(), "");
+ assertThrow(!m_scopes.empty() && m_scopes.back(), OptimizerException, "");
Scope::Identifier const* id = m_scopes.back()->lookup(_originalName);
- solAssert(id, "");
+ assertThrow(id, OptimizerException, "");
if (!m_translations.count(id))
- {
- string translated = _originalName;
- size_t suffix = 0;
- while (m_usedNames.count(translated))
- {
- suffix++;
- translated = _originalName + "_" + std::to_string(suffix);
- }
- m_usedNames.insert(translated);
- m_translations[id] = translated;
- }
+ m_translations[id] = m_nameDispenser.newName(_originalName);
return m_translations.at(id);
}
@@ -79,7 +69,7 @@ void Disambiguator::enterScopeInternal(Scope& _scope)
void Disambiguator::leaveScopeInternal(Scope& _scope)
{
- solAssert(!m_scopes.empty(), "");
- solAssert(m_scopes.back() == &_scope, "");
+ assertThrow(!m_scopes.empty(), OptimizerException, "");
+ assertThrow(m_scopes.back() == &_scope, OptimizerException, "");
m_scopes.pop_back();
}
diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h
index 18ffd157..6fc8a615 100644
--- a/libjulia/optimiser/Disambiguator.h
+++ b/libjulia/optimiser/Disambiguator.h
@@ -23,6 +23,7 @@
#include <libjulia/ASTDataForward.h>
#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/optimiser/NameDispenser.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
@@ -60,7 +61,7 @@ protected:
std::vector<solidity::assembly::Scope*> m_scopes;
std::map<void const*, std::string> m_translations;
- std::set<std::string> m_usedNames;
+ NameDispenser m_nameDispenser;
};
}
diff --git a/libjulia/optimiser/ExpressionInliner.h b/libjulia/optimiser/ExpressionInliner.h
index 10d7659c..3d24ef5d 100644
--- a/libjulia/optimiser/ExpressionInliner.h
+++ b/libjulia/optimiser/ExpressionInliner.h
@@ -23,8 +23,6 @@
#include <libjulia/ASTDataForward.h>
-#include <libsolidity/interface/Exceptions.h>
-
#include <boost/variant.hpp>
#include <boost/optional.hpp>
diff --git a/libjulia/optimiser/ExpressionSimplifier.cpp b/libjulia/optimiser/ExpressionSimplifier.cpp
index 3d471cb3..8bd6b1c7 100644
--- a/libjulia/optimiser/ExpressionSimplifier.cpp
+++ b/libjulia/optimiser/ExpressionSimplifier.cpp
@@ -25,8 +25,6 @@
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Exceptions.h>
-
#include <libdevcore/CommonData.h>
using namespace std;
diff --git a/libjulia/optimiser/FullInliner.cpp b/libjulia/optimiser/FullInliner.cpp
new file mode 100644
index 00000000..e8776e23
--- /dev/null
+++ b/libjulia/optimiser/FullInliner.cpp
@@ -0,0 +1,259 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Optimiser component that performs function inlining for arbitrary functions.
+ */
+
+#include <libjulia/optimiser/FullInliner.h>
+
+#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/optimiser/ASTWalker.h>
+#include <libjulia/optimiser/NameCollector.h>
+#include <libjulia/optimiser/Semantics.h>
+#include <libjulia/Exceptions.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <libdevcore/CommonData.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+using namespace dev::solidity;
+
+FullInliner::FullInliner(Block& _ast):
+ m_ast(_ast)
+{
+ assertThrow(m_ast.statements.size() >= 1, OptimizerException, "");
+ assertThrow(m_ast.statements.front().type() == typeid(Block), OptimizerException, "");
+ m_nameDispenser.m_usedNames = NameCollector(m_ast).names();
+
+ for (size_t i = 1; i < m_ast.statements.size(); ++i)
+ {
+ assertThrow(m_ast.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, "");
+ FunctionDefinition& fun = boost::get<FunctionDefinition>(m_ast.statements.at(i));
+ m_functions[fun.name] = &fun;
+ m_functionsToVisit.insert(&fun);
+ }
+}
+
+void FullInliner::run()
+{
+ assertThrow(m_ast.statements[0].type() == typeid(Block), OptimizerException, "");
+ InlineModifier(*this, m_nameDispenser, "").visit(m_ast.statements[0]);
+ while (!m_functionsToVisit.empty())
+ handleFunction(**m_functionsToVisit.begin());
+}
+
+void FullInliner::handleFunction(FunctionDefinition& _fun)
+{
+ if (!m_functionsToVisit.count(&_fun))
+ return;
+ m_functionsToVisit.erase(&_fun);
+ (InlineModifier(*this, m_nameDispenser, _fun.name))(_fun.body);
+}
+
+void InlineModifier::operator()(FunctionalInstruction& _instruction)
+{
+ visitArguments(_instruction.arguments);
+}
+
+void InlineModifier::operator()(FunctionCall&)
+{
+ assertThrow(false, OptimizerException, "Should be handled in visit() instead.");
+}
+
+void InlineModifier::operator()(ForLoop& _loop)
+{
+ (*this)(_loop.pre);
+ // Do not visit the condition because we cannot inline there.
+ (*this)(_loop.post);
+ (*this)(_loop.body);
+}
+
+void InlineModifier::operator()(Block& _block)
+{
+ // This is only used if needed to minimize the number of move operations.
+ vector<Statement> modifiedStatements;
+ for (size_t i = 0; i < _block.statements.size(); ++i)
+ {
+ visit(_block.statements.at(i));
+ if (!m_statementsToPrefix.empty())
+ {
+ if (modifiedStatements.empty())
+ std::move(
+ _block.statements.begin(),
+ _block.statements.begin() + i,
+ back_inserter(modifiedStatements)
+ );
+ modifiedStatements += std::move(m_statementsToPrefix);
+ m_statementsToPrefix.clear();
+ }
+ if (!modifiedStatements.empty())
+ modifiedStatements.emplace_back(std::move(_block.statements[i]));
+ }
+ if (!modifiedStatements.empty())
+ _block.statements = std::move(modifiedStatements);
+}
+
+void InlineModifier::visit(Expression& _expression)
+{
+ if (_expression.type() != typeid(FunctionCall))
+ return ASTModifier::visit(_expression);
+
+ FunctionCall& funCall = boost::get<FunctionCall>(_expression);
+ FunctionDefinition& fun = m_driver.function(funCall.functionName.name);
+
+ m_driver.handleFunction(fun);
+
+ // TODO: Insert good heuristic here. Perhaps implement that inside the driver.
+ bool doInline = funCall.functionName.name != m_currentFunction;
+
+ if (fun.returnVariables.size() > 1)
+ doInline = false;
+
+ {
+ vector<string> argNames;
+ vector<string> argTypes;
+ for (auto const& arg: fun.parameters)
+ {
+ argNames.push_back(fun.name + "_" + arg.name);
+ argTypes.push_back(arg.type);
+ }
+ visitArguments(funCall.arguments, argNames, argTypes, doInline);
+ }
+
+ if (!doInline)
+ return;
+
+ map<string, string> variableReplacements;
+ for (size_t i = 0; i < funCall.arguments.size(); ++i)
+ variableReplacements[fun.parameters[i].name] = boost::get<Identifier>(funCall.arguments[i]).name;
+ if (fun.returnVariables.empty())
+ _expression = noop(funCall.location);
+ else
+ {
+ string returnVariable = fun.returnVariables[0].name;
+ variableReplacements[returnVariable] = newName(fun.name + "_" + returnVariable);
+
+ m_statementsToPrefix.emplace_back(VariableDeclaration{
+ funCall.location,
+ {{funCall.location, variableReplacements[returnVariable], fun.returnVariables[0].type}},
+ {}
+ });
+ _expression = Identifier{funCall.location, variableReplacements[returnVariable]};
+ }
+ m_statementsToPrefix.emplace_back(BodyCopier(m_nameDispenser, fun.name + "_", variableReplacements)(fun.body));
+}
+
+void InlineModifier::visit(Statement& _statement)
+{
+ ASTModifier::visit(_statement);
+ // Replace pop(0) expression statemets (and others) by empty blocks.
+ if (_statement.type() == typeid(ExpressionStatement))
+ {
+ ExpressionStatement& expSt = boost::get<ExpressionStatement>(_statement);
+ if (expSt.expression.type() == typeid(FunctionalInstruction))
+ {
+ FunctionalInstruction& funInstr = boost::get<FunctionalInstruction>(expSt.expression);
+ if (funInstr.instruction == solidity::Instruction::POP)
+ if (MovableChecker(funInstr.arguments.at(0)).movable())
+ _statement = Block{expSt.location, {}};
+ }
+ }
+}
+
+void InlineModifier::visitArguments(
+ vector<Expression>& _arguments,
+ vector<string> const& _nameHints,
+ vector<string> const& _types,
+ bool _moveToFront
+)
+{
+ // If one of the elements moves parts to the front, all other elements right of it
+ // also have to be moved to the front to keep the order of evaluation.
+ vector<Statement> prefix;
+ for (size_t i = 0; i < _arguments.size(); ++i)
+ {
+ auto& arg = _arguments[i];
+ // TODO optimize vector operations, check that it actually moves
+ auto internalPrefix = visitRecursively(arg);
+ if (!internalPrefix.empty())
+ {
+ _moveToFront = true;
+ // We go through the arguments left to right, so we have to invert
+ // the prefixes.
+ prefix = std::move(internalPrefix) + std::move(prefix);
+ }
+ else if (_moveToFront)
+ {
+ auto location = locationOf(arg);
+ string var = newName(i < _nameHints.size() ? _nameHints[i] : "");
+ prefix.emplace(prefix.begin(), VariableDeclaration{
+ location,
+ {{TypedName{location, var, i < _types.size() ? _types[i] : ""}}},
+ make_shared<Expression>(std::move(arg))
+ });
+ arg = Identifier{location, var};
+ }
+ }
+ m_statementsToPrefix += std::move(prefix);
+}
+
+vector<Statement> InlineModifier::visitRecursively(Expression& _expression)
+{
+ vector<Statement> saved;
+ saved.swap(m_statementsToPrefix);
+ visit(_expression);
+ saved.swap(m_statementsToPrefix);
+ return saved;
+}
+
+string InlineModifier::newName(string const& _prefix)
+{
+ return m_nameDispenser.newName(_prefix);
+}
+
+Expression InlineModifier::noop(SourceLocation const& _location)
+{
+ return FunctionalInstruction{_location, solidity::Instruction::POP, {
+ Literal{_location, assembly::LiteralKind::Number, "0", ""}
+ }};
+}
+
+Statement BodyCopier::operator()(VariableDeclaration const& _varDecl)
+{
+ for (auto const& var: _varDecl.variables)
+ m_variableReplacements[var.name] = m_nameDispenser.newName(m_varNamePrefix + var.name);
+ return ASTCopier::operator()(_varDecl);
+}
+
+Statement BodyCopier::operator()(FunctionDefinition const& _funDef)
+{
+ assertThrow(false, OptimizerException, "Function hoisting has to be done before function inlining.");
+ return _funDef;
+}
+
+string BodyCopier::translateIdentifier(string const& _name)
+{
+ if (m_variableReplacements.count(_name))
+ return m_variableReplacements.at(_name);
+ else
+ return _name;
+}
diff --git a/libjulia/optimiser/FullInliner.h b/libjulia/optimiser/FullInliner.h
new file mode 100644
index 00000000..ff9e6854
--- /dev/null
+++ b/libjulia/optimiser/FullInliner.h
@@ -0,0 +1,179 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Optimiser component that performs function inlining for arbitrary functions.
+ */
+#pragma once
+
+#include <libjulia/ASTDataForward.h>
+
+#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/optimiser/ASTWalker.h>
+#include <libjulia/optimiser/NameDispenser.h>
+#include <libjulia/Exceptions.h>
+
+#include <libevmasm/SourceLocation.h>
+
+#include <boost/variant.hpp>
+#include <boost/optional.hpp>
+
+#include <set>
+
+namespace dev
+{
+namespace julia
+{
+
+class NameCollector;
+
+
+/**
+ * Optimiser component that modifies an AST in place, inlining arbitrary functions.
+ *
+ * Code of the form
+ *
+ * function f(a, b) -> c { ... }
+ * h(g(x(...), f(arg1(...), arg2(...)), y(...)), z(...))
+ *
+ * is transformed into
+ *
+ * function f(a, b) -> c { ... }
+ *
+ * let z1 := z(...) let y1 := y(...) let a2 := arg2(...) let a1 := arg1(...)
+ * let c1 := 0
+ * { code of f, with replacements: a -> a1, b -> a2, c -> c1, d -> d1 }
+ * h(g(x(...), c1, y1), z1)
+ *
+ * No temporary variable is created for expressions that are "movable"
+ * (i.e. they are "pure", have no side-effects and also do not depend on other code
+ * that might have side-effects).
+ *
+ * This component can only be used on sources with unique names and with hoisted functions,
+ * i.e. the root node has to be a block that itself contains a single block followed by all
+ * function definitions.
+ */
+class FullInliner: public ASTModifier
+{
+public:
+ explicit FullInliner(Block& _ast);
+
+ void run();
+
+ /// Perform inlining operations inside the given function.
+ void handleFunction(FunctionDefinition& _function);
+
+ FunctionDefinition& function(std::string _name) { return *m_functions.at(_name); }
+
+private:
+ /// The AST to be modified. The root block itself will not be modified, because
+ /// we store pointers to functions.
+ Block& m_ast;
+ std::map<std::string, FunctionDefinition*> m_functions;
+ std::set<FunctionDefinition*> m_functionsToVisit;
+ NameDispenser m_nameDispenser;
+};
+
+/**
+ * Class that walks the AST of a block that does not contain function definitions and perform
+ * the actual code modifications.
+ */
+class InlineModifier: public ASTModifier
+{
+public:
+ InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, std::string _functionName):
+ m_currentFunction(std::move(_functionName)),
+ m_driver(_driver),
+ m_nameDispenser(_nameDispenser)
+ { }
+ ~InlineModifier()
+ {
+ assertThrow(m_statementsToPrefix.empty(), OptimizerException, "");
+ }
+
+ virtual void operator()(FunctionalInstruction&) override;
+ virtual void operator()(FunctionCall&) override;
+ virtual void operator()(ForLoop&) override;
+ virtual void operator()(Block& _block) override;
+
+ using ASTModifier::visit;
+ virtual void visit(Expression& _expression) override;
+ virtual void visit(Statement& _st) override;
+
+private:
+
+ /// Visits a list of expressions (usually an argument list to a function call) and tries
+ /// to inline them. If one of them is inlined, all right of it have to be moved to the front
+ /// (to keep the order of evaluation). If @a _moveToFront is true, all elements are moved
+ /// to the front. @a _nameHints and @_types are used for the newly created variables, but
+ /// both can be empty.
+ void visitArguments(
+ std::vector<Expression>& _arguments,
+ std::vector<std::string> const& _nameHints = {},
+ std::vector<std::string> const& _types = {},
+ bool _moveToFront = false
+ );
+
+ /// Visits an expression, but saves and restores the current statements to prefix and returns
+ /// the statements that should be prefixed for @a _expression.
+ std::vector<Statement> visitRecursively(Expression& _expression);
+
+ std::string newName(std::string const& _prefix);
+
+ /// @returns an expression returning nothing.
+ Expression noop(SourceLocation const& _location);
+
+ /// List of statements that should go in front of the currently visited AST element,
+ /// at the statement level.
+ std::vector<Statement> m_statementsToPrefix;
+ std::string m_currentFunction;
+ FullInliner& m_driver;
+ NameDispenser& m_nameDispenser;
+};
+
+/**
+ * Creates a copy of a block that is supposed to be the body of a function.
+ * Applies replacements to referenced variables and creates new names for
+ * variable declarations.
+ */
+class BodyCopier: public ASTCopier
+{
+public:
+ BodyCopier(
+ NameDispenser& _nameDispenser,
+ std::string const& _varNamePrefix,
+ std::map<std::string, std::string> const& _variableReplacements
+ ):
+ m_nameDispenser(_nameDispenser),
+ m_varNamePrefix(_varNamePrefix),
+ m_variableReplacements(_variableReplacements)
+ {}
+
+ using ASTCopier::operator ();
+
+ virtual Statement operator()(VariableDeclaration const& _varDecl) override;
+ virtual Statement operator()(FunctionDefinition const& _funDef) override;
+
+ virtual std::string translateIdentifier(std::string const& _name) override;
+
+ NameDispenser& m_nameDispenser;
+ std::string const& m_varNamePrefix;
+ std::map<std::string, std::string> m_variableReplacements;
+};
+
+
+}
+}
diff --git a/libjulia/optimiser/FunctionGrouper.cpp b/libjulia/optimiser/FunctionGrouper.cpp
index cc40bc46..f1e99e6b 100644
--- a/libjulia/optimiser/FunctionGrouper.cpp
+++ b/libjulia/optimiser/FunctionGrouper.cpp
@@ -23,8 +23,6 @@
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Exceptions.h>
-
#include <boost/range/algorithm_ext/erase.hpp>
using namespace std;
diff --git a/libjulia/optimiser/FunctionHoister.cpp b/libjulia/optimiser/FunctionHoister.cpp
index 63f6edb9..98fc714c 100644
--- a/libjulia/optimiser/FunctionHoister.cpp
+++ b/libjulia/optimiser/FunctionHoister.cpp
@@ -25,8 +25,6 @@
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Exceptions.h>
-
#include <libdevcore/CommonData.h>
using namespace std;
diff --git a/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp b/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp
index 2097e091..e237063d 100644
--- a/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp
+++ b/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp
@@ -20,9 +20,9 @@
#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h>
-#include <libsolidity/inlineasm/AsmData.h>
+#include <libjulia/optimiser/Utilities.h>
-#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/inlineasm/AsmData.h>
using namespace std;
using namespace dev;
@@ -56,7 +56,7 @@ void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _fu
// We cannot overwrite previous settings, because this function definition
// would not be valid here if we were searching inside a functionally inlinable
// function body.
- solAssert(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, "");
+ assertThrow(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, OptimizerException, "");
m_disallowedIdentifiers = set<string>{retVariable, _function.name};
boost::apply_visitor(*this, *assignment.value);
if (!m_foundDisallowedIdentifier)
diff --git a/libjulia/optimiser/InlinableExpressionFunctionFinder.h b/libjulia/optimiser/InlinableExpressionFunctionFinder.h
index 36cb557a..d11160d7 100644
--- a/libjulia/optimiser/InlinableExpressionFunctionFinder.h
+++ b/libjulia/optimiser/InlinableExpressionFunctionFinder.h
@@ -23,8 +23,6 @@
#include <libjulia/ASTDataForward.h>
#include <libjulia/optimiser/ASTWalker.h>
-#include <libsolidity/interface/Exceptions.h>
-
#include <set>
namespace dev
diff --git a/libjulia/optimiser/MainFunction.cpp b/libjulia/optimiser/MainFunction.cpp
new file mode 100644
index 00000000..bcd2f178
--- /dev/null
+++ b/libjulia/optimiser/MainFunction.cpp
@@ -0,0 +1,54 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Changes the topmost block to be a function with a specific name ("main") which has no
+ * inputs nor outputs.
+ */
+
+#include <libjulia/optimiser/MainFunction.h>
+
+#include <libjulia/optimiser/NameCollector.h>
+#include <libjulia/Exceptions.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+#include <libdevcore/CommonData.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+using namespace dev::solidity;
+
+void MainFunction::operator()(Block& _block)
+{
+ assertThrow(_block.statements.size() >= 1, OptimizerException, "");
+ assertThrow(_block.statements[0].type() == typeid(Block), OptimizerException, "");
+ for (size_t i = 1; i < _block.statements.size(); ++i)
+ assertThrow(_block.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, "");
+ /// @todo this should handle scopes properly and instead of an assertion it should rename the conflicting function
+ assertThrow(NameCollector(_block).names().count("main") == 0, OptimizerException, "");
+
+ Block& block = boost::get<Block>(_block.statements[0]);
+ FunctionDefinition main{
+ block.location,
+ "main",
+ {},
+ {},
+ std::move(block)
+ };
+ _block.statements[0] = std::move(main);
+}
diff --git a/libjulia/optimiser/MainFunction.h b/libjulia/optimiser/MainFunction.h
new file mode 100644
index 00000000..7201d89a
--- /dev/null
+++ b/libjulia/optimiser/MainFunction.h
@@ -0,0 +1,41 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Changes the topmost block to be a function with a specific name ("main") which has no
+ * inputs nor outputs.
+ */
+
+#pragma once
+
+#include <libjulia/ASTDataForward.h>
+
+namespace dev
+{
+namespace julia
+{
+
+/**
+ * Prerequisites: Function Grouper
+ */
+class MainFunction
+{
+public:
+ void operator()(Block& _block);
+};
+
+}
+}
diff --git a/libjulia/optimiser/NameCollector.cpp b/libjulia/optimiser/NameCollector.cpp
index 510ee289..c0d0b707 100644
--- a/libjulia/optimiser/NameCollector.cpp
+++ b/libjulia/optimiser/NameCollector.cpp
@@ -35,7 +35,6 @@ void NameCollector::operator()(VariableDeclaration const& _varDecl)
void NameCollector::operator ()(FunctionDefinition const& _funDef)
{
m_names.insert(_funDef.name);
- m_functions[_funDef.name] = &_funDef;
for (auto const arg: _funDef.parameters)
m_names.insert(arg.name);
for (auto const ret: _funDef.returnVariables)
diff --git a/libjulia/optimiser/NameCollector.h b/libjulia/optimiser/NameCollector.h
index 2d4a1d4b..29856172 100644
--- a/libjulia/optimiser/NameCollector.h
+++ b/libjulia/optimiser/NameCollector.h
@@ -37,15 +37,18 @@ namespace julia
class NameCollector: public ASTWalker
{
public:
+ explicit NameCollector(Block const& _block)
+ {
+ (*this)(_block);
+ }
+
using ASTWalker::operator ();
virtual void operator()(VariableDeclaration const& _varDecl) override;
virtual void operator()(FunctionDefinition const& _funDef) override;
- std::set<std::string> const& names() const { return m_names; }
- std::map<std::string, FunctionDefinition const*> const& functions() const { return m_functions; }
+ std::set<std::string> names() const { return m_names; }
private:
std::set<std::string> m_names;
- std::map<std::string, FunctionDefinition const*> m_functions;
};
/**
diff --git a/libjulia/optimiser/NameDispenser.cpp b/libjulia/optimiser/NameDispenser.cpp
new file mode 100644
index 00000000..e4f0e4f6
--- /dev/null
+++ b/libjulia/optimiser/NameDispenser.cpp
@@ -0,0 +1,38 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Optimiser component that can create new unique names.
+ */
+
+#include <libjulia/optimiser/NameDispenser.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+
+string NameDispenser::newName(string const& _prefix)
+{
+ string name = _prefix;
+ size_t suffix = 0;
+ while (name.empty() || m_usedNames.count(name))
+ {
+ suffix++;
+ name = _prefix + "_" + std::to_string(suffix);
+ }
+ m_usedNames.insert(name);
+ return name;
+}
diff --git a/libjulia/optimiser/NameDispenser.h b/libjulia/optimiser/NameDispenser.h
new file mode 100644
index 00000000..91c43d54
--- /dev/null
+++ b/libjulia/optimiser/NameDispenser.h
@@ -0,0 +1,37 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Optimiser component that can create new unique names.
+ */
+#pragma once
+
+#include <set>
+#include <string>
+
+namespace dev
+{
+namespace julia
+{
+
+struct NameDispenser
+{
+ std::string newName(std::string const& _prefix);
+ std::set<std::string> m_usedNames;
+};
+
+}
+}
diff --git a/libjulia/optimiser/README.md b/libjulia/optimiser/README.md
index e7134440..e8aa777a 100644
--- a/libjulia/optimiser/README.md
+++ b/libjulia/optimiser/README.md
@@ -87,3 +87,12 @@ simple rules like ``x + 0 == x`` to simplify expressions.
## Ineffective Statement Remover
This step removes statements that have no side-effects.
+
+## WebAssembly specific
+
+### Main Function
+
+Changes the topmost block to be a function with a specific name ("main") which has no
+inputs nor outputs.
+
+Depends on the Function Grouper.
diff --git a/libjulia/optimiser/Rematerialiser.cpp b/libjulia/optimiser/Rematerialiser.cpp
index eaa75e33..392099fb 100644
--- a/libjulia/optimiser/Rematerialiser.cpp
+++ b/libjulia/optimiser/Rematerialiser.cpp
@@ -22,6 +22,7 @@
#include <libjulia/optimiser/Metrics.h>
#include <libjulia/optimiser/ASTCopier.h>
+#include <libjulia/Exceptions.h>
#include <libsolidity/inlineasm/AsmData.h>
@@ -44,7 +45,7 @@ void Rematerialiser::visit(Expression& _e)
expressionValid = false;
break;
}
- solAssert(m_value.at(name), "");
+ assertThrow(m_value.at(name), OptimizerException, "");
auto const& value = *m_value.at(name);
if (expressionValid && CodeSize::codeSize(value) <= 7)
_e = (ASTCopier{}).translate(value);
diff --git a/libjulia/optimiser/Semantics.cpp b/libjulia/optimiser/Semantics.cpp
index 92728c46..f28925a4 100644
--- a/libjulia/optimiser/Semantics.cpp
+++ b/libjulia/optimiser/Semantics.cpp
@@ -20,6 +20,8 @@
#include <libjulia/optimiser/Semantics.h>
+#include <libjulia/Exceptions.h>
+
#include <libsolidity/inlineasm/AsmData.h>
#include <libevmasm/SemanticInformation.h>
@@ -56,5 +58,5 @@ void MovableChecker::operator()(FunctionCall const&)
void MovableChecker::visit(Statement const&)
{
- solAssert(false, "Movability for statement requested.");
+ assertThrow(false, OptimizerException, "Movability for statement requested.");
}
diff --git a/libjulia/optimiser/SyntacticalEquality.cpp b/libjulia/optimiser/SyntacticalEquality.cpp
index 2b90b091..c497336d 100644
--- a/libjulia/optimiser/SyntacticalEquality.cpp
+++ b/libjulia/optimiser/SyntacticalEquality.cpp
@@ -20,8 +20,9 @@
#include <libjulia/optimiser/SyntacticalEquality.h>
+#include <libjulia/Exceptions.h>
+
#include <libsolidity/inlineasm/AsmData.h>
-#include <libsolidity/interface/Exceptions.h>
#include <libdevcore/CommonData.h>
@@ -62,7 +63,7 @@ bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const&
}
else
{
- solAssert(false, "Invlid expression");
+ assertThrow(false, OptimizerException, "Invalid expression");
}
return false;
}
diff --git a/libjulia/optimiser/UnusedPruner.cpp b/libjulia/optimiser/UnusedPruner.cpp
index 50038431..54e8fd6e 100644
--- a/libjulia/optimiser/UnusedPruner.cpp
+++ b/libjulia/optimiser/UnusedPruner.cpp
@@ -23,6 +23,7 @@
#include <libjulia/optimiser/NameCollector.h>
#include <libjulia/optimiser/Semantics.h>
#include <libjulia/optimiser/Utilities.h>
+#include <libjulia/Exceptions.h>
#include <libsolidity/inlineasm/AsmData.h>
@@ -108,8 +109,8 @@ void UnusedPruner::subtractReferences(map<string, size_t> const& _subtrahend)
{
for (auto const& ref: _subtrahend)
{
- solAssert(m_references.count(ref.first), "");
- solAssert(m_references.at(ref.first) >= ref.second, "");
+ assertThrow(m_references.count(ref.first), OptimizerException, "");
+ assertThrow(m_references.at(ref.first) >= ref.second, OptimizerException, "");
m_references[ref.first] -= ref.second;
m_shouldRunAgain = true;
}
diff --git a/libjulia/optimiser/Utilities.h b/libjulia/optimiser/Utilities.h
index e3b4b087..88ba3f47 100644
--- a/libjulia/optimiser/Utilities.h
+++ b/libjulia/optimiser/Utilities.h
@@ -22,16 +22,11 @@
#include <libjulia/ASTDataForward.h>
-#include <libdevcore/Exceptions.h>
-
namespace dev
{
namespace julia
{
-struct IuliaException: virtual Exception {};
-struct OptimizerException: virtual IuliaException {};
-
/// Removes statements that are just empty blocks (non-recursive).
void removeEmptyBlocks(Block& _block);
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index 97b01c83..0bdec4b4 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -28,7 +28,7 @@ else()
endif()
add_library(solidity ${sources} ${headers})
-target_link_libraries(solidity PUBLIC evmasm devcore)
+target_link_libraries(solidity PUBLIC evmasm devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp
new file mode 100644
index 00000000..6edf7986
--- /dev/null
+++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp
@@ -0,0 +1,156 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/analysis/ControlFlowAnalyzer.h>
+
+using namespace std;
+using namespace dev::solidity;
+
+bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot)
+{
+ _astRoot.accept(*this);
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
+}
+
+bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
+{
+ auto const& functionFlow = m_cfg.functionFlow(_function);
+ checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit);
+ return false;
+}
+
+set<VariableDeclaration const*> ControlFlowAnalyzer::variablesAssignedInNode(CFGNode const *node)
+{
+ set<VariableDeclaration const*> result;
+ for (auto expression: node->block.expressions)
+ {
+ if (auto const* assignment = dynamic_cast<Assignment const*>(expression))
+ {
+ stack<Expression const*> expressions;
+ expressions.push(&assignment->leftHandSide());
+ while (!expressions.empty())
+ {
+ Expression const* expression = expressions.top();
+ expressions.pop();
+
+ if (auto const *tuple = dynamic_cast<TupleExpression const*>(expression))
+ for (auto const& component: tuple->components())
+ expressions.push(component.get());
+ else if (auto const* identifier = dynamic_cast<Identifier const*>(expression))
+ if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(
+ identifier->annotation().referencedDeclaration
+ ))
+ result.insert(variableDeclaration);
+ }
+ }
+ }
+ return result;
+}
+
+void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
+ FunctionDefinition const& _function,
+ CFGNode const* _functionEntry,
+ CFGNode const* _functionExit
+) const
+{
+ if (_function.returnParameterList()->parameters().empty())
+ return;
+
+ map<CFGNode const*, set<VariableDeclaration const*>> unassigned;
+
+ {
+ auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
+ for (auto const& returnParameter: _function.returnParameterList()->parameters())
+ if (returnParameter->type()->dataStoredIn(DataLocation::Storage))
+ unassignedAtFunctionEntry.insert(returnParameter.get());
+ }
+
+ stack<CFGNode const*> nodesToTraverse;
+ nodesToTraverse.push(_functionEntry);
+
+ // walk all paths from entry with maximal set of unassigned return values
+ while (!nodesToTraverse.empty())
+ {
+ auto node = nodesToTraverse.top();
+ nodesToTraverse.pop();
+
+ auto& unassignedAtNode = unassigned[node];
+
+ if (node->block.returnStatement != nullptr)
+ if (node->block.returnStatement->expression())
+ unassignedAtNode.clear();
+ if (!unassignedAtNode.empty())
+ {
+ // kill all return values to which a value is assigned
+ for (auto const* variableDeclaration: variablesAssignedInNode(node))
+ unassignedAtNode.erase(variableDeclaration);
+
+ // kill all return values referenced in inline assembly
+ // a reference is enough, checking whether there actually was an assignment might be overkill
+ for (auto assembly: node->block.inlineAssemblyStatements)
+ for (auto const& ref: assembly->annotation().externalReferences)
+ if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
+ unassignedAtNode.erase(variableDeclaration);
+ }
+
+ for (auto const& exit: node->exits)
+ {
+ auto& unassignedAtExit = unassigned[exit];
+ auto oldSize = unassignedAtExit.size();
+ unassignedAtExit.insert(unassignedAtNode.begin(), unassignedAtNode.end());
+ // (re)traverse an exit, if we are on a path with new unassigned return values to consider
+ // this will terminate, since there is only a finite number of unassigned return values
+ if (unassignedAtExit.size() > oldSize)
+ nodesToTraverse.push(exit);
+ }
+ }
+
+ if (!unassigned[_functionExit].empty())
+ {
+ vector<VariableDeclaration const*> unassignedOrdered(
+ unassigned[_functionExit].begin(),
+ unassigned[_functionExit].end()
+ );
+ sort(
+ unassignedOrdered.begin(),
+ unassignedOrdered.end(),
+ [](VariableDeclaration const* lhs, VariableDeclaration const* rhs) -> bool {
+ return lhs->id() < rhs->id();
+ }
+ );
+ for (auto const* returnVal: unassignedOrdered)
+ {
+ SecondarySourceLocation ssl;
+ for (CFGNode* lastNodeBeforeExit: _functionExit->entries)
+ if (unassigned[lastNodeBeforeExit].count(returnVal))
+ {
+ if (!!lastNodeBeforeExit->block.returnStatement)
+ ssl.append("Problematic return:", lastNodeBeforeExit->block.returnStatement->location());
+ else
+ ssl.append("Problematic end of function:", _function.location());
+ }
+
+ m_errorReporter.warning(
+ returnVal->location(),
+ "This variable is of storage pointer type and might be returned without assignment. "
+ "This can cause storage corruption. Assign the variable (potentially from itself) "
+ "to remove this warning.",
+ ssl
+ );
+ }
+ }
+}
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h
new file mode 100644
index 00000000..43e13fb6
--- /dev/null
+++ b/libsolidity/analysis/ControlFlowAnalyzer.h
@@ -0,0 +1,52 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/analysis/ControlFlowGraph.h>
+
+#include <set>
+
+namespace dev
+{
+namespace solidity
+{
+
+class ControlFlowAnalyzer: private ASTConstVisitor
+{
+public:
+ explicit ControlFlowAnalyzer(CFG const& _cfg, ErrorReporter& _errorReporter):
+ m_cfg(_cfg), m_errorReporter(_errorReporter) {}
+
+ bool analyze(ASTNode const& _astRoot);
+
+ virtual bool visit(FunctionDefinition const& _function) override;
+
+private:
+ static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node);
+ void checkUnassignedStorageReturnValues(
+ FunctionDefinition const& _function,
+ CFGNode const* _functionEntry,
+ CFGNode const* _functionExit
+ ) const;
+
+ CFG const& m_cfg;
+ ErrorReporter& m_errorReporter;
+};
+
+}
+}
diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp
new file mode 100644
index 00000000..35d7687c
--- /dev/null
+++ b/libsolidity/analysis/ControlFlowBuilder.cpp
@@ -0,0 +1,370 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/analysis/ControlFlowBuilder.h>
+
+using namespace dev;
+using namespace solidity;
+using namespace std;
+
+ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
+ m_nodeContainer(_nodeContainer), m_currentFunctionFlow(_functionFlow), m_currentNode(_functionFlow.entry)
+{
+}
+
+unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
+ CFG::NodeContainer& _nodeContainer,
+ FunctionDefinition const& _function
+)
+{
+ auto functionFlow = unique_ptr<FunctionFlow>(new FunctionFlow());
+ functionFlow->entry = _nodeContainer.newNode();
+ functionFlow->exit = _nodeContainer.newNode();
+ functionFlow->revert = _nodeContainer.newNode();
+ ControlFlowBuilder builder(_nodeContainer, *functionFlow);
+ builder.appendControlFlow(_function);
+ connect(builder.m_currentNode, functionFlow->exit);
+ return functionFlow;
+}
+
+
+unique_ptr<ModifierFlow> ControlFlowBuilder::createModifierFlow(
+ CFG::NodeContainer& _nodeContainer,
+ ModifierDefinition const& _modifier
+)
+{
+ auto modifierFlow = unique_ptr<ModifierFlow>(new ModifierFlow());
+ modifierFlow->entry = _nodeContainer.newNode();
+ modifierFlow->exit = _nodeContainer.newNode();
+ modifierFlow->revert = _nodeContainer.newNode();
+ modifierFlow->placeholderEntry = _nodeContainer.newNode();
+ modifierFlow->placeholderExit = _nodeContainer.newNode();
+ ControlFlowBuilder builder(_nodeContainer, *modifierFlow);
+ builder.appendControlFlow(_modifier);
+ connect(builder.m_currentNode, modifierFlow->exit);
+ return modifierFlow;
+}
+
+bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
+{
+ solAssert(!!m_currentNode, "");
+
+ switch(_operation.getOperator())
+ {
+ case Token::Or:
+ case Token::And:
+ {
+ appendControlFlow(_operation.leftExpression());
+
+ auto nodes = splitFlow<2>();
+ nodes[0] = createFlow(nodes[0], _operation.rightExpression());
+ mergeFlow(nodes, nodes[1]);
+
+ return false;
+ }
+ default:
+ break;
+ }
+ return ASTConstVisitor::visit(_operation);
+}
+
+bool ControlFlowBuilder::visit(Conditional const& _conditional)
+{
+ solAssert(!!m_currentNode, "");
+
+ _conditional.condition().accept(*this);
+
+ auto nodes = splitFlow<2>();
+
+ nodes[0] = createFlow(nodes[0], _conditional.trueExpression());
+ nodes[1] = createFlow(nodes[1], _conditional.falseExpression());
+
+ mergeFlow(nodes);
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(IfStatement const& _ifStatement)
+{
+ solAssert(!!m_currentNode, "");
+
+ _ifStatement.condition().accept(*this);
+
+ auto nodes = splitFlow<2>();
+ nodes[0] = createFlow(nodes[0], _ifStatement.trueStatement());
+
+ if (_ifStatement.falseStatement())
+ {
+ nodes[1] = createFlow(nodes[1], *_ifStatement.falseStatement());
+ mergeFlow(nodes);
+ }
+ else
+ mergeFlow(nodes, nodes[1]);
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(ForStatement const& _forStatement)
+{
+ solAssert(!!m_currentNode, "");
+
+ if (_forStatement.initializationExpression())
+ _forStatement.initializationExpression()->accept(*this);
+
+ auto condition = createLabelHere();
+
+ if (_forStatement.condition())
+ appendControlFlow(*_forStatement.condition());
+
+ auto loopExpression = newLabel();
+ auto nodes = splitFlow<2>();
+ auto afterFor = nodes[1];
+ m_currentNode = nodes[0];
+
+ {
+ BreakContinueScope scope(*this, afterFor, loopExpression);
+ appendControlFlow(_forStatement.body());
+ }
+
+ placeAndConnectLabel(loopExpression);
+
+ if (auto expression = _forStatement.loopExpression())
+ appendControlFlow(*expression);
+
+ connect(m_currentNode, condition);
+ m_currentNode = afterFor;
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement)
+{
+ solAssert(!!m_currentNode, "");
+
+ if (_whileStatement.isDoWhile())
+ {
+ auto afterWhile = newLabel();
+ auto whileBody = createLabelHere();
+
+ {
+ // Note that "continue" in this case currently indeed jumps to whileBody
+ // and not to the condition. This is inconsistent with JavaScript and C and
+ // therefore a bug. This will be fixed in the future (planned for 0.5.0)
+ // and the Control Flow Graph will have to be adjusted accordingly.
+ BreakContinueScope scope(*this, afterWhile, whileBody);
+ appendControlFlow(_whileStatement.body());
+ }
+ appendControlFlow(_whileStatement.condition());
+
+ connect(m_currentNode, whileBody);
+ placeAndConnectLabel(afterWhile);
+ }
+ else
+ {
+ auto whileCondition = createLabelHere();
+
+ appendControlFlow(_whileStatement.condition());
+
+ auto nodes = splitFlow<2>();
+
+ auto whileBody = nodes[0];
+ auto afterWhile = nodes[1];
+
+ m_currentNode = whileBody;
+ {
+ BreakContinueScope scope(*this, afterWhile, whileCondition);
+ appendControlFlow(_whileStatement.body());
+ }
+
+ connect(m_currentNode, whileCondition);
+
+ m_currentNode = afterWhile;
+ }
+
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Break const&)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!m_breakJump, "");
+ connect(m_currentNode, m_breakJump);
+ m_currentNode = newLabel();
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Continue const&)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!m_continueJump, "");
+ connect(m_currentNode, m_continueJump);
+ m_currentNode = newLabel();
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Throw const&)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!m_currentFunctionFlow.revert, "");
+ connect(m_currentNode, m_currentFunctionFlow.revert);
+ m_currentNode = newLabel();
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Block const&)
+{
+ solAssert(!!m_currentNode, "");
+ createLabelHere();
+ return true;
+}
+
+void ControlFlowBuilder::endVisit(Block const&)
+{
+ solAssert(!!m_currentNode, "");
+ createLabelHere();
+}
+
+bool ControlFlowBuilder::visit(Return const& _return)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!m_currentFunctionFlow.exit, "");
+ solAssert(!m_currentNode->block.returnStatement, "");
+ m_currentNode->block.returnStatement = &_return;
+ connect(m_currentNode, m_currentFunctionFlow.exit);
+ m_currentNode = newLabel();
+ return true;
+}
+
+
+bool ControlFlowBuilder::visit(PlaceholderStatement const&)
+{
+ solAssert(!!m_currentNode, "");
+ auto modifierFlow = dynamic_cast<ModifierFlow const*>(&m_currentFunctionFlow);
+ solAssert(!!modifierFlow, "");
+
+ connect(m_currentNode, modifierFlow->placeholderEntry);
+
+ m_currentNode = newLabel();
+
+ connect(modifierFlow->placeholderExit, m_currentNode);
+ return false;
+}
+
+bool ControlFlowBuilder::visitNode(ASTNode const& node)
+{
+ solAssert(!!m_currentNode, "");
+ if (auto const* expression = dynamic_cast<Expression const*>(&node))
+ m_currentNode->block.expressions.emplace_back(expression);
+ else if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(&node))
+ m_currentNode->block.variableDeclarations.emplace_back(variableDeclaration);
+ else if (auto const* assembly = dynamic_cast<InlineAssembly const*>(&node))
+ m_currentNode->block.inlineAssemblyStatements.emplace_back(assembly);
+
+ return true;
+}
+
+bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!_functionCall.expression().annotation().type, "");
+
+ if (auto functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type))
+ switch (functionType->kind())
+ {
+ case FunctionType::Kind::Revert:
+ solAssert(!!m_currentFunctionFlow.revert, "");
+ _functionCall.expression().accept(*this);
+ ASTNode::listAccept(_functionCall.arguments(), *this);
+ connect(m_currentNode, m_currentFunctionFlow.revert);
+ m_currentNode = newLabel();
+ return false;
+ case FunctionType::Kind::Require:
+ case FunctionType::Kind::Assert:
+ {
+ solAssert(!!m_currentFunctionFlow.revert, "");
+ _functionCall.expression().accept(*this);
+ ASTNode::listAccept(_functionCall.arguments(), *this);
+ connect(m_currentNode, m_currentFunctionFlow.revert);
+ auto nextNode = newLabel();
+ connect(m_currentNode, nextNode);
+ m_currentNode = nextNode;
+ return false;
+ }
+ default:
+ break;
+ }
+ return ASTConstVisitor::visit(_functionCall);
+}
+
+void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
+{
+ _node.accept(*this);
+}
+
+CFGNode* ControlFlowBuilder::createFlow(CFGNode* _entry, ASTNode const& _node)
+{
+ auto oldCurrentNode = m_currentNode;
+ m_currentNode = _entry;
+ appendControlFlow(_node);
+ auto endNode = m_currentNode;
+ m_currentNode = oldCurrentNode;
+ return endNode;
+}
+
+void ControlFlowBuilder::connect(CFGNode* _from, CFGNode* _to)
+{
+ solAssert(_from, "");
+ solAssert(_to, "");
+ _from->exits.push_back(_to);
+ _to->entries.push_back(_from);
+}
+
+CFGNode* ControlFlowBuilder::newLabel()
+{
+ return m_nodeContainer.newNode();
+}
+
+CFGNode* ControlFlowBuilder::createLabelHere()
+{
+ auto label = m_nodeContainer.newNode();
+ connect(m_currentNode, label);
+ m_currentNode = label;
+ return label;
+}
+
+void ControlFlowBuilder::placeAndConnectLabel(CFGNode* _node)
+{
+ connect(m_currentNode, _node);
+ m_currentNode = _node;
+}
+
+ControlFlowBuilder::BreakContinueScope::BreakContinueScope(
+ ControlFlowBuilder& _parser,
+ CFGNode* _breakJump,
+ CFGNode* _continueJump
+): m_parser(_parser), m_origBreakJump(_parser.m_breakJump), m_origContinueJump(_parser.m_continueJump)
+{
+ m_parser.m_breakJump = _breakJump;
+ m_parser.m_continueJump = _continueJump;
+}
+
+ControlFlowBuilder::BreakContinueScope::~BreakContinueScope()
+{
+ m_parser.m_breakJump = m_origBreakJump;
+ m_parser.m_continueJump = m_origContinueJump;
+}
diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h
new file mode 100644
index 00000000..e9d96e5f
--- /dev/null
+++ b/libsolidity/analysis/ControlFlowBuilder.h
@@ -0,0 +1,143 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/analysis/ControlFlowGraph.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+#include <array>
+#include <memory>
+
+namespace dev {
+namespace solidity {
+
+/** Helper class that builds the control flow of a function or modifier.
+ * Modifiers are not yet applied to the functions. This is done in a second
+ * step in the CFG class.
+ */
+class ControlFlowBuilder: private ASTConstVisitor
+{
+public:
+ static std::unique_ptr<FunctionFlow> createFunctionFlow(
+ CFG::NodeContainer& _nodeContainer,
+ FunctionDefinition const& _function
+ );
+ static std::unique_ptr<ModifierFlow> createModifierFlow(
+ CFG::NodeContainer& _nodeContainer,
+ ModifierDefinition const& _modifier
+ );
+
+private:
+ explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
+
+ virtual bool visit(BinaryOperation const& _operation) override;
+ virtual bool visit(Conditional const& _conditional) override;
+ virtual bool visit(IfStatement const& _ifStatement) override;
+ virtual bool visit(ForStatement const& _forStatement) override;
+ virtual bool visit(WhileStatement const& _whileStatement) override;
+ virtual bool visit(Break const&) override;
+ virtual bool visit(Continue const&) override;
+ virtual bool visit(Throw const&) override;
+ virtual bool visit(Block const&) override;
+ virtual void endVisit(Block const&) override;
+ virtual bool visit(Return const& _return) override;
+ virtual bool visit(PlaceholderStatement const&) override;
+ virtual bool visit(FunctionCall const& _functionCall) override;
+
+
+ /// Appends the control flow of @a _node to the current control flow.
+ void appendControlFlow(ASTNode const& _node);
+
+ /// Starts at @a _entry and parses the control flow of @a _node.
+ /// @returns The node at which the parsed control flow ends.
+ /// m_currentNode is not affected (it is saved and restored).
+ CFGNode* createFlow(CFGNode* _entry, ASTNode const& _node);
+
+ /// Creates an arc from @a _from to @a _to.
+ static void connect(CFGNode* _from, CFGNode* _to);
+
+
+protected:
+ virtual bool visitNode(ASTNode const& node) override;
+
+private:
+
+ /// Splits the control flow starting at the current node into n paths.
+ /// m_currentNode is set to nullptr and has to be set manually or
+ /// using mergeFlow later.
+ template<size_t n>
+ std::array<CFGNode*, n> splitFlow()
+ {
+ std::array<CFGNode*, n> result;
+ for (auto& node: result)
+ {
+ node = m_nodeContainer.newNode();
+ connect(m_currentNode, node);
+ }
+ m_currentNode = nullptr;
+ return result;
+ }
+
+ /// Merges the control flow of @a _nodes to @a _endNode.
+ /// If @a _endNode is nullptr, a new node is creates and used as end node.
+ /// Sets the merge destination as current node.
+ /// Note: @a _endNode may be one of the nodes in @a _nodes.
+ template<size_t n>
+ void mergeFlow(std::array<CFGNode*, n> const& _nodes, CFGNode* _endNode = nullptr)
+ {
+ CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode;
+ for (auto& node: _nodes)
+ if (node != mergeDestination)
+ connect(node, mergeDestination);
+ m_currentNode = mergeDestination;
+ }
+
+ CFGNode* newLabel();
+ CFGNode* createLabelHere();
+ void placeAndConnectLabel(CFGNode *_node);
+
+ CFG::NodeContainer& m_nodeContainer;
+
+ /// The control flow of the function that is currently parsed.
+ /// Note: this can also be a ModifierFlow
+ FunctionFlow const& m_currentFunctionFlow;
+
+ CFGNode* m_currentNode = nullptr;
+
+ /// The current jump destination of break Statements.
+ CFGNode* m_breakJump = nullptr;
+ /// The current jump destination of continue Statements.
+ CFGNode* m_continueJump = nullptr;
+
+ /// Helper class that replaces the break and continue jump destinations for the
+ /// current scope and restores the originals at the end of the scope.
+ class BreakContinueScope
+ {
+ public:
+ BreakContinueScope(ControlFlowBuilder& _parser, CFGNode* _breakJump, CFGNode* _continueJump);
+ ~BreakContinueScope();
+ private:
+ ControlFlowBuilder& m_parser;
+ CFGNode* m_origBreakJump;
+ CFGNode* m_origContinueJump;
+ };
+};
+
+}
+}
diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp
new file mode 100644
index 00000000..9b3da0eb
--- /dev/null
+++ b/libsolidity/analysis/ControlFlowGraph.cpp
@@ -0,0 +1,136 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <libsolidity/analysis/ControlFlowGraph.h>
+#include <libsolidity/analysis/ControlFlowBuilder.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+#include <algorithm>
+
+using namespace std;
+using namespace dev::solidity;
+
+bool CFG::constructFlow(ASTNode const& _astRoot)
+{
+ _astRoot.accept(*this);
+ applyModifiers();
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
+}
+
+
+bool CFG::visit(ModifierDefinition const& _modifier)
+{
+ m_modifierControlFlow[&_modifier] = ControlFlowBuilder::createModifierFlow(m_nodeContainer, _modifier);
+ return false;
+}
+
+bool CFG::visit(FunctionDefinition const& _function)
+{
+ m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
+ return false;
+}
+
+FunctionFlow const& CFG::functionFlow(FunctionDefinition const& _function) const
+{
+ solAssert(m_functionControlFlow.count(&_function), "");
+ return *m_functionControlFlow.find(&_function)->second;
+}
+
+CFGNode* CFG::NodeContainer::newNode()
+{
+ m_nodes.emplace_back(new CFGNode());
+ return m_nodes.back().get();
+}
+
+void CFG::applyModifiers()
+{
+ for (auto const& function: m_functionControlFlow)
+ {
+ for (auto const& modifierInvocation: boost::adaptors::reverse(function.first->modifiers()))
+ {
+ if (auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
+ modifierInvocation->name()->annotation().referencedDeclaration
+ ))
+ {
+ solAssert(m_modifierControlFlow.count(modifierDefinition), "");
+ applyModifierFlowToFunctionFlow(*m_modifierControlFlow[modifierDefinition], function.second.get());
+ }
+ }
+ }
+}
+
+void CFG::applyModifierFlowToFunctionFlow(
+ ModifierFlow const& _modifierFlow,
+ FunctionFlow* _functionFlow
+)
+{
+ solAssert(!!_functionFlow, "");
+
+ map<CFGNode*, CFGNode*> copySrcToCopyDst;
+
+ // inherit the revert node of the function
+ copySrcToCopyDst[_modifierFlow.revert] = _functionFlow->revert;
+
+ // replace the placeholder nodes by the function entry and exit
+ copySrcToCopyDst[_modifierFlow.placeholderEntry] = _functionFlow->entry;
+ copySrcToCopyDst[_modifierFlow.placeholderExit] = _functionFlow->exit;
+
+ stack<CFGNode*> nodesToCopy;
+ nodesToCopy.push(_modifierFlow.entry);
+
+ // map the modifier entry to a new node that will become the new function entry
+ copySrcToCopyDst[_modifierFlow.entry] = m_nodeContainer.newNode();
+
+ while (!nodesToCopy.empty())
+ {
+ CFGNode* copySrcNode = nodesToCopy.top();
+ nodesToCopy.pop();
+
+ solAssert(copySrcToCopyDst.count(copySrcNode), "");
+
+ CFGNode* copyDstNode = copySrcToCopyDst[copySrcNode];
+
+ copyDstNode->block = copySrcNode->block;
+ for (auto const& entry: copySrcNode->entries)
+ {
+ if (!copySrcToCopyDst.count(entry))
+ {
+ copySrcToCopyDst[entry] = m_nodeContainer.newNode();
+ nodesToCopy.push(entry);
+ }
+ copyDstNode->entries.emplace_back(copySrcToCopyDst[entry]);
+ }
+ for (auto const& exit: copySrcNode->exits)
+ {
+ if (!copySrcToCopyDst.count(exit))
+ {
+ copySrcToCopyDst[exit] = m_nodeContainer.newNode();
+ nodesToCopy.push(exit);
+ }
+ copyDstNode->exits.emplace_back(copySrcToCopyDst[exit]);
+ }
+ }
+
+ // if the modifier control flow never reached its exit node,
+ // we need to create a new (disconnected) exit node now
+ if (!copySrcToCopyDst.count(_modifierFlow.exit))
+ copySrcToCopyDst[_modifierFlow.exit] = m_nodeContainer.newNode();
+
+ _functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry];
+ _functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit];
+} \ No newline at end of file
diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h
new file mode 100644
index 00000000..c646e4f1
--- /dev/null
+++ b/libsolidity/analysis/ControlFlowGraph.h
@@ -0,0 +1,148 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/interface/ErrorReporter.h>
+
+#include <map>
+#include <memory>
+#include <stack>
+#include <vector>
+
+namespace dev
+{
+namespace solidity
+{
+
+/** Basic Control Flow Block.
+ * Basic block of control flow. Consists of a set of (unordered) AST nodes
+ * for which control flow is always linear. A basic control flow block
+ * encompasses at most one scope. Reverts are considered to break the control
+ * flow.
+ * @todo Handle function calls correctly. So far function calls are not considered
+ * to change the control flow.
+ */
+struct ControlFlowBlock
+{
+ /// All variable declarations inside this control flow block.
+ std::vector<VariableDeclaration const*> variableDeclarations;
+ /// All expressions inside this control flow block (this includes all subexpressions!).
+ std::vector<Expression const*> expressions;
+ /// All inline assembly statements inside in this control flow block.
+ std::vector<InlineAssembly const*> inlineAssemblyStatements;
+ /// If control flow returns in this node, the return statement is stored in returnStatement,
+ /// otherwise returnStatement is nullptr.
+ Return const* returnStatement = nullptr;
+};
+
+/** Node of the Control Flow Graph.
+ * The control flow is a directed graph connecting control flow blocks.
+ * An arc between two nodes indicates that the control flow can possibly
+ * move from its start node to its end node during execution.
+ */
+struct CFGNode
+{
+ /// Entry nodes. All CFG nodes from which control flow may move into this node.
+ std::vector<CFGNode*> entries;
+ /// Exit nodes. All CFG nodes to which control flow may continue after this node.
+ std::vector<CFGNode*> exits;
+
+ /// Control flow in the node.
+ ControlFlowBlock block;
+};
+
+/** Describes the control flow of a function. */
+struct FunctionFlow
+{
+ virtual ~FunctionFlow() {}
+ /// Entry node. Control flow of the function starts here.
+ /// This node is empty and does not have any entries.
+ CFGNode* entry = nullptr;
+ /// Exit node. All non-reverting control flow of the function ends here.
+ /// This node is empty and does not have any exits, but may have multiple entries
+ /// (e.g. all return statements of the function).
+ CFGNode* exit = nullptr;
+ /// Revert node. Control flow of the function in case of revert.
+ /// This node is empty does not have any exits, but may have multiple entries
+ /// (e.g. all assert, require, revert and throw statements).
+ CFGNode* revert = nullptr;
+};
+
+/** Describes the control flow of a modifier.
+ * Every placeholder breaks the control flow. The node preceding the
+ * placeholder is assigned placeholderEntry as exit and the node
+ * following the placeholder is assigned placeholderExit as entry.
+ */
+struct ModifierFlow: FunctionFlow
+{
+ /// Control flow leading towards a placeholder exit in placeholderEntry.
+ CFGNode* placeholderEntry = nullptr;
+ /// Control flow coming from a placeholder enter from placeholderExit.
+ CFGNode* placeholderExit = nullptr;
+};
+
+class CFG: private ASTConstVisitor
+{
+public:
+ explicit CFG(ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
+
+ bool constructFlow(ASTNode const& _astRoot);
+
+ virtual bool visit(ModifierDefinition const& _modifier) override;
+ virtual bool visit(FunctionDefinition const& _function) override;
+
+ FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
+
+ class NodeContainer
+ {
+ public:
+ CFGNode* newNode();
+ private:
+ std::vector<std::unique_ptr<CFGNode>> m_nodes;
+ };
+private:
+ /// Initially the control flow for all functions *ignoring* modifiers and for
+ /// all modifiers is constructed. Afterwards the control flow of functions
+ /// is adjusted by applying all modifiers.
+ void applyModifiers();
+
+ /// Creates a copy of the modifier flow @a _modifierFlow, while replacing the
+ /// placeholder entry and exit with the function entry and exit, as well as
+ /// replacing the modifier revert node with the function's revert node.
+ /// The resulting control flow is the new function flow with the modifier applied.
+ /// @a _functionFlow is updated in-place.
+ void applyModifierFlowToFunctionFlow(
+ ModifierFlow const& _modifierFlow,
+ FunctionFlow* _functionFlow
+ );
+
+ ErrorReporter& m_errorReporter;
+
+ /// Node container.
+ /// All nodes allocated during the construction of the control flow graph
+ /// are owned by the CFG class and stored in this container.
+ NodeContainer m_nodeContainer;
+
+ std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
+ std::map<ModifierDefinition const*, std::unique_ptr<ModifierFlow>> m_modifierControlFlow;
+};
+
+}
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 72d29762..30302908 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1067,6 +1067,7 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
{
if (
_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall ||
+ type(_emit.eventCall().expression())->category() != Type::Category::Function ||
dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event
)
m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation.");
@@ -1075,6 +1076,7 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
if (!_statement.initialValue())
{
// No initial value is only permitted for single variables with specified type.
@@ -1091,7 +1093,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
solAssert(m_scope, "");
- if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
+ if (v050)
m_errorReporter.declarationError(varDecl.location(), errorText);
else
m_errorReporter.warning(varDecl.location(), errorText);
@@ -1131,12 +1133,33 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
") in value for variable assignment (0) needed"
);
}
- else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back())
- m_errorReporter.fatalTypeError(
- _statement.location(),
- "Wildcard both at beginning and end of variable declaration list is only allowed "
- "if the number of components is equal."
- );
+ else if (valueTypes.size() != variables.size())
+ {
+ if (v050)
+ m_errorReporter.fatalTypeError(
+ _statement.location(),
+ "Different number of components on the left hand side (" +
+ toString(variables.size()) +
+ ") than on the right hand side (" +
+ toString(valueTypes.size()) +
+ ")."
+ );
+ else if (!variables.front() && !variables.back())
+ m_errorReporter.fatalTypeError(
+ _statement.location(),
+ "Wildcard both at beginning and end of variable declaration list is only allowed "
+ "if the number of components is equal."
+ );
+ else
+ m_errorReporter.warning(
+ _statement.location(),
+ "Different number of components on the left hand side (" +
+ toString(variables.size()) +
+ ") than on the right hand side (" +
+ toString(valueTypes.size()) +
+ ")."
+ );
+ }
size_t minNumValues = variables.size();
if (!variables.empty() && (!variables.back() || !variables.front()))
--minNumValues;
@@ -1200,8 +1223,9 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
string extension;
if (auto type = dynamic_cast<IntegerType const*>(var.annotation().type.get()))
{
- int numBits = type->numBits();
+ unsigned numBits = type->numBits();
bool isSigned = type->isSigned();
+ solAssert(numBits > 0, "");
string minValue;
string maxValue;
if (isSigned)
@@ -1333,6 +1357,7 @@ bool TypeChecker::visit(Conditional const& _conditional)
bool TypeChecker::visit(Assignment const& _assignment)
{
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
requireLValue(_assignment.leftHandSide());
TypePointer t = type(_assignment.leftHandSide());
_assignment.annotation().type = t;
@@ -1345,11 +1370,29 @@ bool TypeChecker::visit(Assignment const& _assignment)
);
// Sequenced assignments of tuples is not valid, make the result a "void" type.
_assignment.annotation().type = make_shared<TupleType>();
+
expectType(_assignment.rightHandSide(), *tupleType);
// expectType does not cause fatal errors, so we have to check again here.
- if (dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
+ if (TupleType const* rhsType = dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
+ {
checkDoubleStorageAssignment(_assignment);
+ // @todo For 0.5.0, this code shoud move to TupleType::isImplicitlyConvertibleTo,
+ // but we cannot do it right now.
+ if (rhsType->components().size() != tupleType->components().size())
+ {
+ string message =
+ "Different number of components on the left hand side (" +
+ toString(tupleType->components().size()) +
+ ") than on the right hand side (" +
+ toString(rhsType->components().size()) +
+ ").";
+ if (v050)
+ m_errorReporter.typeError(_assignment.location(), message);
+ else
+ m_errorReporter.warning(_assignment.location(), message);
+ }
+ }
}
else if (t->category() == Type::Category::Mapping)
{
@@ -1406,8 +1449,10 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
}
else
{
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
bool isPure = true;
TypePointer inlineArrayType;
+
for (size_t i = 0; i < components.size(); ++i)
{
// Outside of an lvalue-context, the only situation where a component can be empty is (x,).
@@ -1418,6 +1463,17 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
components[i]->accept(*this);
types.push_back(type(*components[i]));
+ if (types[i]->category() == Type::Category::Tuple)
+ if (dynamic_cast<TupleType const&>(*types[i]).components().empty())
+ {
+ if (_tuple.isInlineArray())
+ m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty.");
+ if (v050)
+ m_errorReporter.fatalTypeError(components[i]->location(), "Tuple component cannot be empty.");
+ else
+ m_errorReporter.warning(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<RationalNumberType const&>(*types[i]).mobileType())
@@ -1648,12 +1704,22 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else
_functionCall.annotation().type = make_shared<TupleType>(returnTypes);
+ bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
+ string msg;
if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::SHA3)
- m_errorReporter.warning(_functionCall.location(), "\"sha3\" has been deprecated in favour of \"keccak256\"");
+ msg = "\"sha3\" has been deprecated in favour of \"keccak256\"";
else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct)
- m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\"");
+ msg = "\"suicide\" has been deprecated in favour of \"selfdestruct\"";
+ if (!msg.empty())
+ {
+ if (v050)
+ m_errorReporter.typeError(_functionCall.location(), msg);
+ else
+ m_errorReporter.warning(_functionCall.location(), msg);
+ }
}
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
{
@@ -1674,18 +1740,55 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
{
/* If no mobile type is available an error will be raised elsewhere. */
if (literal->mobileType())
- m_errorReporter.warning(
- arguments[i]->location(),
- "The type of \"" +
- argType->toString() +
- "\" was inferred as " +
- literal->mobileType()->toString() +
- ". This is probably not desired. Use an explicit type to silence this warning."
- );
+ {
+ if (v050)
+ m_errorReporter.typeError(
+ arguments[i]->location(),
+ "Cannot perform packed encoding for a literal. Please convert it to an explicit type first."
+ );
+ else
+ m_errorReporter.warning(
+ arguments[i]->location(),
+ "The type of \"" +
+ argType->toString() +
+ "\" was inferred as " +
+ literal->mobileType()->toString() +
+ ". This is probably not desired. Use an explicit type to silence this warning."
+ );
+ }
}
}
}
+ if (functionType->takesSinglePackedBytesParameter())
+ {
+ if (
+ (arguments.size() > 1) ||
+ (arguments.size() == 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
+ )
+ {
+ string msg =
+ "This function only accepts a single \"bytes\" argument. Please use "
+ "\"abi.encodePacked(...)\" or a similar function to encode the data.";
+ if (v050)
+ m_errorReporter.typeError(_functionCall.location(), msg);
+ else
+ m_errorReporter.warning(_functionCall.location(), msg);
+ }
+
+ if (arguments.size() == 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
+ {
+ string msg =
+ "The provided argument of type " +
+ type(*arguments.front())->toString() +
+ " is not implicitly convertible to expected type bytes memory.";
+ if (v050)
+ m_errorReporter.typeError(_functionCall.location(), msg);
+ else
+ m_errorReporter.warning(_functionCall.location(), msg);
+ }
+ }
+
if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
@@ -2019,6 +2122,9 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (auto tt = dynamic_cast<TypeType const*>(exprType.get()))
if (tt->actualType()->category() == Type::Category::Enum)
annotation.isPure = true;
+ if (auto magicType = dynamic_cast<MagicType const*>(exprType.get()))
+ if (magicType->kind() == MagicType::Kind::ABI)
+ annotation.isPure = true;
return false;
}
@@ -2202,6 +2308,7 @@ void TypeChecker::endVisit(Literal const& _literal)
"For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals"
);
}
+
if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
{
if (v050)
@@ -2217,6 +2324,21 @@ void TypeChecker::endVisit(Literal const& _literal)
"You can use an expression of the form \"0x1234 * 1 day\" instead."
);
}
+
+ if (_literal.subDenomination() == Literal::SubDenomination::Year)
+ {
+ if (v050)
+ m_errorReporter.typeError(
+ _literal.location(),
+ "Using \"years\" as a unit denomination is deprecated."
+ );
+ else
+ m_errorReporter.warning(
+ _literal.location(),
+ "Using \"years\" as a unit denomination is deprecated."
+ );
+ }
+
if (!_literal.annotation().type)
_literal.annotation().type = Type::forLiteral(_literal);
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index a53987bf..fa0d6921 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -146,6 +146,7 @@ private:
class Scopable
{
public:
+ virtual ~Scopable() = default;
/// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step.
ASTNode const* scope() const { return m_scope; }
@@ -307,6 +308,7 @@ private:
class VariableScope
{
public:
+ virtual ~VariableScope() = default;
void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
std::vector<VariableDeclaration const*> const& localVariables() const { return m_localVariables; }
@@ -320,6 +322,7 @@ private:
class Documented
{
public:
+ virtual ~Documented() = default;
explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {}
/// @return A shared pointer of an ASTString.
@@ -336,6 +339,7 @@ protected:
class ImplementationOptional
{
public:
+ virtual ~ImplementationOptional() = default;
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
/// @return whether this node is fully implemented or not
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
index b1389f0f..6c0ce6f8 100644
--- a/libsolidity/ast/ASTVisitor.h
+++ b/libsolidity/ast/ASTVisitor.h
@@ -43,6 +43,7 @@ namespace solidity
class ASTVisitor
{
public:
+ virtual ~ASTVisitor() = default;
virtual bool visit(SourceUnit& _node) { return visitNode(_node); }
virtual bool visit(PragmaDirective& _node) { return visitNode(_node); }
virtual bool visit(ImportDirective& _node) { return visitNode(_node); }
@@ -147,6 +148,7 @@ protected:
class ASTConstVisitor
{
public:
+ virtual ~ASTConstVisitor() = default;
virtual bool visit(SourceUnit const& _node) { return visitNode(_node); }
virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); }
virtual bool visit(ImportDirective const& _node) { return visitNode(_node); }
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 425e5045..60e3183c 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -425,14 +425,14 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT
}
-IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
+IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
{
if (isAddress())
solAssert(m_bits == 160, "");
solAssert(
m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0,
- "Invalid bit number for integer type: " + dev::toString(_bits)
+ "Invalid bit number for integer type: " + dev::toString(m_bits)
);
}
@@ -584,7 +584,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
{
if (isAddress())
return {
- {"balance", make_shared<IntegerType >(256)},
+ {"balance", make_shared<IntegerType>(256)},
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)},
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
@@ -595,13 +595,12 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
return MemberList::MemberMap();
}
-FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPointType::Modifier _modifier):
+FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, FixedPointType::Modifier _modifier):
m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_modifier(_modifier)
{
solAssert(
- 8 <= m_totalBits && m_totalBits <= 256 && m_totalBits % 8 == 0 &&
- 0 <= m_fractionalDigits && m_fractionalDigits <= 80,
- "Invalid bit number(s) for fixed type: " +
+ 8 <= m_totalBits && m_totalBits <= 256 && m_totalBits % 8 == 0 && m_fractionalDigits <= 80,
+ "Invalid bit number(s) for fixed type: " +
dev::toString(_totalBits) + "x" + dev::toString(_fractionalDigits)
);
}
@@ -627,8 +626,7 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() ||
- _convertTo.category() == Category::Integer ||
- _convertTo.category() == Category::FixedBytes;
+ (_convertTo.category() == Category::Integer && !dynamic_cast<IntegerType const&>(_convertTo).isAddress());
}
TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const
@@ -682,13 +680,7 @@ bigint FixedPointType::minIntegerValue() const
TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
- if (
- _other->category() != Category::RationalNumber &&
- _other->category() != category() &&
- _other->category() != Category::Integer
- )
- return TypePointer();
- auto commonType = Type::commonType(shared_from_this(), _other); //might be fixed point or integer
+ auto commonType = Type::commonType(shared_from_this(), _other);
if (!commonType)
return TypePointer();
@@ -696,19 +688,16 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi
// All fixed types can be compared
if (Token::isCompareOp(_operator))
return commonType;
- if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator))
+ if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator) || _operator == Token::Exp)
return TypePointer();
- if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
- {
- if (Token::Exp == _operator)
- return TypePointer();
- }
- else if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
- if (intType->isAddress())
- return TypePointer();
return commonType;
}
+std::shared_ptr<IntegerType> FixedPointType::asIntegerType() const
+{
+ return make_shared<IntegerType>(numBits(), isSigned() ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned);
+}
+
tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
{
rational value;
@@ -860,7 +849,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (isFractional())
return false;
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
- int forSignBit = (targetType.isSigned() ? 1 : 0);
+ unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
if (m_value > rational(0))
{
if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit)))
@@ -1148,7 +1137,7 @@ u256 RationalNumberType::literalValue(Literal const*) const
auto fixed = fixedPointType();
solAssert(fixed, "");
int fractionalDigits = fixed->fractionalDigits();
- shiftedValue = (m_value.numerator() / m_value.denominator()) * pow(bigint(10), fractionalDigits);
+ shiftedValue = m_value.numerator() * pow(bigint(10), fractionalDigits) / m_value.denominator();
}
// we ignore the literal and hope that the type was correctly determined
@@ -1274,17 +1263,12 @@ bool StringLiteralType::isValidUTF8() const
return dev::validateUTF8(m_value);
}
-shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
+FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes)
{
- if (_literal.length() <= 32)
- return make_shared<FixedBytesType>(_literal.length());
- return shared_ptr<FixedBytesType>();
-}
-
-FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes)
-{
- solAssert(m_bytes >= 0 && m_bytes <= 32,
- "Invalid byte number for fixed bytes type: " + dev::toString(m_bytes));
+ solAssert(
+ m_bytes > 0 && m_bytes <= 32,
+ "Invalid byte number for fixed bytes type: " + dev::toString(m_bytes)
+ );
}
bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@@ -2881,7 +2865,11 @@ bool FunctionType::isPure() const
m_kind == Kind::RIPEMD160 ||
m_kind == Kind::AddMod ||
m_kind == Kind::MulMod ||
- m_kind == Kind::ObjectCreation;
+ m_kind == Kind::ObjectCreation ||
+ m_kind == Kind::ABIEncode ||
+ m_kind == Kind::ABIEncodePacked ||
+ m_kind == Kind::ABIEncodeWithSelector ||
+ m_kind == Kind::ABIEncodeWithSignature;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 345f84a1..95821634 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -138,6 +138,7 @@ private:
class Type: private boost::noncopyable, public std::enable_shared_from_this<Type>
{
public:
+ virtual ~Type() = default;
enum class Category
{
Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
@@ -318,7 +319,7 @@ public:
};
virtual Category category() const override { return Category::Integer; }
- explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned);
+ explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
@@ -341,7 +342,7 @@ public:
virtual TypePointer encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
- int numBits() const { return m_bits; }
+ unsigned numBits() const { return m_bits; }
bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
@@ -349,7 +350,7 @@ public:
bigint maxValue() const;
private:
- int m_bits;
+ unsigned m_bits;
Modifier m_modifier;
};
@@ -365,7 +366,7 @@ public:
};
virtual Category category() const override { return Category::FixedPoint; }
- explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
+ explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
@@ -385,9 +386,9 @@ public:
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
/// Number of bits used for this type in total.
- int numBits() const { return m_totalBits; }
+ unsigned numBits() const { return m_totalBits; }
/// Number of decimal digits after the radix point.
- int fractionalDigits() const { return m_fractionalDigits; }
+ unsigned fractionalDigits() const { return m_fractionalDigits; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
/// @returns the largest integer value this type con hold. Note that this is not the
/// largest value in general.
@@ -396,9 +397,12 @@ public:
/// smallest value in general.
bigint minIntegerValue() const;
+ /// @returns the smallest integer type that can hold this type with fractional parts shifted to integers.
+ std::shared_ptr<IntegerType> asIntegerType() const;
+
private:
- int m_totalBits;
- int m_fractionalDigits;
+ unsigned m_totalBits;
+ unsigned m_fractionalDigits;
Modifier m_modifier;
};
@@ -502,11 +506,7 @@ class FixedBytesType: public Type
public:
virtual Category category() const override { return Category::FixedBytes; }
- /// @returns the smallest bytes type for the given literal or an empty pointer
- /// if no type fits.
- static std::shared_ptr<FixedBytesType> smallestTypeForLiteral(std::string const& _literal);
-
- explicit FixedBytesType(int _bytes);
+ explicit FixedBytesType(unsigned _bytes);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
@@ -524,10 +524,10 @@ public:
virtual TypePointer encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
- int numBytes() const { return m_bytes; }
+ unsigned numBytes() const { return m_bytes; }
private:
- int m_bytes;
+ unsigned m_bytes;
};
/**
@@ -1046,8 +1046,8 @@ public:
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
- /// @returns true if the result of this function only depends on its arguments
- /// and it does not modify the state.
+ /// @returns true if the result of this function only depends on its arguments,
+ /// does not modify the state and is a compile-time constant.
/// Currently, this will only return true for internal functions like keccak and ecrecover.
bool isPure() const;
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
@@ -1058,6 +1058,22 @@ public:
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); }
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
+ /// true iff the function takes a single bytes parameter and it is passed on without padding.
+ /// @todo until 0.5.0, this is just a "recommendation".
+ bool takesSinglePackedBytesParameter() const
+ {
+ // @todo add the call kinds here with 0.5.0 and perhaps also log0.
+ switch (m_kind)
+ {
+ case FunctionType::Kind::SHA3:
+ case FunctionType::Kind::SHA256:
+ case FunctionType::Kind::RIPEMD160:
+ return true;
+ default:
+ return false;
+ }
+ }
+
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
bool bound() const { return m_bound; }
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 8e890854..3e3aa0ae 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -371,7 +371,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
if (toCategory == Type::Category::Integer)
body =
Whiskers("converted := <convert>(<shift>(value))")
- ("shift", shiftRightFunction(256 - from.numBytes() * 8, false))
+ ("shift", shiftRightFunction(256 - from.numBytes() * 8))
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
.render();
else
@@ -458,8 +458,8 @@ string ABIFunctions::splitExternalFunctionIdFunction()
}
)")
("functionName", functionName)
- ("shr32", shiftRightFunction(32, false))
- ("shr64", shiftRightFunction(64, false))
+ ("shr32", shiftRightFunction(32))
+ ("shr64", shiftRightFunction(64))
.render();
});
}
@@ -831,7 +831,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
for (size_t i = 0; i < itemsPerSlot; ++i)
- items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8, false);
+ items[i]["shiftRightFun"] = shiftRightFunction(i * storageBytes * 8);
templ("items", items);
return templ.render();
}
@@ -927,7 +927,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
}
else
memberTempl("preprocess", "");
- memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8, false) + "(slotValue)");
+ memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8) + "(slotValue)");
}
else
{
@@ -1401,37 +1401,75 @@ string ABIFunctions::copyToMemoryFunction(bool _fromCalldata)
string ABIFunctions::shiftLeftFunction(size_t _numBits)
{
+ solAssert(_numBits < 256, "");
+
string functionName = "shift_left_" + to_string(_numBits);
- return createFunction(functionName, [&]() {
- solAssert(_numBits < 256, "");
- return
- Whiskers(R"(
- function <functionName>(value) -> newValue {
- newValue := mul(value, <multiplier>)
- }
- )")
- ("functionName", functionName)
- ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
- .render();
- });
+ if (m_evmVersion.hasBitwiseShifting())
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := shl(<numBits>, value)
+ }
+ )")
+ ("functionName", functionName)
+ ("numBits", to_string(_numBits))
+ .render();
+ });
+ }
+ else
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := mul(value, <multiplier>)
+ }
+ )")
+ ("functionName", functionName)
+ ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
+ .render();
+ });
+ }
}
-string ABIFunctions::shiftRightFunction(size_t _numBits, bool _signed)
+string ABIFunctions::shiftRightFunction(size_t _numBits)
{
- string functionName = "shift_right_" + to_string(_numBits) + (_signed ? "_signed" : "_unsigned");
- return createFunction(functionName, [&]() {
- solAssert(_numBits < 256, "");
- return
- Whiskers(R"(
- function <functionName>(value) -> newValue {
- newValue := <div>(value, <multiplier>)
- }
- )")
- ("functionName", functionName)
- ("div", _signed ? "sdiv" : "div")
- ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
- .render();
- });
+ solAssert(_numBits < 256, "");
+
+ // Note that if this is extended with signed shifts,
+ // the opcodes SAR and SDIV behave differently with regards to rounding!
+
+ string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
+ if (m_evmVersion.hasBitwiseShifting())
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := shr(<numBits>, value)
+ }
+ )")
+ ("functionName", functionName)
+ ("numBits", to_string(_numBits))
+ .render();
+ });
+ }
+ else
+ {
+ return createFunction(functionName, [&]() {
+ return
+ Whiskers(R"(
+ function <functionName>(value) -> newValue {
+ newValue := div(value, <multiplier>)
+ }
+ )")
+ ("functionName", functionName)
+ ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
+ .render();
+ });
+ }
}
string ABIFunctions::roundUpFunction()
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index 2b582e84..db4d40f5 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -22,6 +22,8 @@
#pragma once
+#include <libsolidity/interface/EVMVersion.h>
+
#include <libsolidity/ast/ASTForward.h>
#include <vector>
@@ -48,6 +50,8 @@ using TypePointers = std::vector<TypePointer>;
class ABIFunctions
{
public:
+ explicit ABIFunctions(EVMVersion _evmVersion = EVMVersion{}) : m_evmVersion(_evmVersion) {}
+
/// @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: <headStart> <value_n> ... <value_1>, i.e.
@@ -191,7 +195,7 @@ private:
std::string copyToMemoryFunction(bool _fromCalldata);
std::string shiftLeftFunction(size_t _numBits);
- std::string shiftRightFunction(size_t _numBits, bool _signed);
+ std::string shiftRightFunction(size_t _numBits);
/// @returns the name of a function that rounds its input to the next multiple
/// of 32 or the input if it is a multiple of 32.
std::string roundUpFunction();
@@ -225,6 +229,8 @@ private:
/// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions;
+
+ EVMVersion m_evmVersion;
};
}
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 098472f7..5776b5d1 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -55,7 +55,8 @@ public:
explicit CompilerContext(EVMVersion _evmVersion = EVMVersion{}, CompilerContext* _runtimeContext = nullptr):
m_asm(std::make_shared<eth::Assembly>()),
m_evmVersion(_evmVersion),
- m_runtimeContext(_runtimeContext)
+ m_runtimeContext(_runtimeContext),
+ m_abiFunctions(m_evmVersion)
{
if (m_runtimeContext)
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 4af7d905..d9f17263 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -89,7 +89,6 @@ void CompilerUtils::revertWithStringData(Type const& _argumentType)
abiEncode({_argumentType.shared_from_this()}, {make_shared<ArrayType>(DataLocation::Memory, true)});
toSizeAfterFreeMemoryPointer();
m_context << Instruction::REVERT;
- m_context.adjustStackOffset(_argumentType.sizeOnStack());
}
unsigned CompilerUtils::loadFromMemory(
@@ -110,7 +109,7 @@ void CompilerUtils::loadFromMemoryDynamic(
bool _padToWordBoundaries,
bool _keepUpdatedMemoryOffset
)
-{
+{
if (_keepUpdatedMemoryOffset)
m_context << Instruction::DUP1;
@@ -396,7 +395,7 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++;
- solAssert((argSize + dynPointers) < 16, "Stack too deep, try using less variables.");
+ solAssert((argSize + dynPointers) < 16, "Stack too deep, try using fewer variables.");
}
else
{
@@ -599,15 +598,15 @@ void CompilerUtils::splitExternalFunctionType(bool _leftAligned)
if (_leftAligned)
{
m_context << Instruction::DUP1;
- rightShiftNumberOnStack(64 + 32, false);
+ rightShiftNumberOnStack(64 + 32);
// <input> <address>
m_context << Instruction::SWAP1;
- rightShiftNumberOnStack(64, false);
+ rightShiftNumberOnStack(64);
}
else
{
m_context << Instruction::DUP1;
- rightShiftNumberOnStack(32, false);
+ rightShiftNumberOnStack(32);
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
}
m_context << u256(0xffffffffUL) << Instruction::AND;
@@ -675,7 +674,7 @@ void CompilerUtils::convertType(
// conversion from bytes to integer. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
- rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8, false);
+ rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8);
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
@@ -688,7 +687,7 @@ void CompilerUtils::convertType(
m_context << Instruction::POP << u256(0);
else if (targetType.numBytes() > typeOnStack.numBytes() || _cleanupNeeded)
{
- int bytes = min(typeOnStack.numBytes(), targetType.numBytes());
+ unsigned bytes = min(typeOnStack.numBytes(), targetType.numBytes());
m_context << ((u256(1) << (256 - bytes * 8)) - 1);
m_context << Instruction::NOT << Instruction::AND;
}
@@ -741,7 +740,7 @@ void CompilerUtils::convertType(
else if (targetTypeCategory == Type::Category::FixedPoint)
{
solAssert(
- stackTypeCategory == Type::Category::Integer ||
+ stackTypeCategory == Type::Category::Integer ||
stackTypeCategory == Type::Category::RationalNumber ||
stackTypeCategory == Type::Category::FixedPoint,
"Invalid conversion to FixedMxNType requested."
@@ -796,7 +795,7 @@ void CompilerUtils::convertType(
bytesConstRef data(value);
if (targetTypeCategory == Type::Category::FixedBytes)
{
- int const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes();
+ unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes();
solAssert(data.size() <= 32, "");
m_context << (h256::Arith(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes))));
}
@@ -1242,7 +1241,7 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
bool leftAligned = _type.category() == Type::Category::FixedBytes;
// add leading or trailing zeros by dividing/multiplying depending on alignment
int shiftFactor = (32 - numBytes) * 8;
- rightShiftNumberOnStack(shiftFactor, false);
+ rightShiftNumberOnStack(shiftFactor);
if (leftAligned)
leftShiftNumberOnStack(shiftFactor);
}
@@ -1265,13 +1264,20 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
{
solAssert(_bits < 256, "");
- m_context << (u256(1) << _bits) << Instruction::MUL;
+ if (m_context.evmVersion().hasBitwiseShifting())
+ m_context << _bits << Instruction::SHL;
+ else
+ m_context << (u256(1) << _bits) << Instruction::MUL;
}
-void CompilerUtils::rightShiftNumberOnStack(unsigned _bits, bool _isSigned)
+void CompilerUtils::rightShiftNumberOnStack(unsigned _bits)
{
solAssert(_bits < 256, "");
- m_context << (u256(1) << _bits) << Instruction::SWAP1 << (_isSigned ? Instruction::SDIV : Instruction::DIV);
+ // NOTE: If we add signed right shift, SAR rounds differently than SDIV
+ if (m_context.evmVersion().hasBitwiseShifting())
+ m_context << _bits << Instruction::SHR;
+ else
+ m_context << (u256(1) << _bits) << Instruction::SWAP1 << Instruction::DIV;
}
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords)
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 476a7559..8e3a8a5d 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -254,7 +254,7 @@ public:
/// Helper function to shift top value on the stack to the right.
/// Stack pre: <value> <shift_by_bits>
/// Stack post: <shifted_value>
- void rightShiftNumberOnStack(unsigned _bits, bool _isSigned = false);
+ void rightShiftNumberOnStack(unsigned _bits);
/// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type.
void computeHashStatic();
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 3cf46a9d..4bcc1fa9 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -548,7 +548,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (m_context.runtimeContext())
// We have a runtime context, so we need the creation part.
- utils().rightShiftNumberOnStack(32, false);
+ utils().rightShiftNumberOnStack(32);
else
// Extract the runtime part.
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
@@ -933,7 +933,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// condition was not met, flag an error
m_context.appendInvalid();
else if (arguments.size() > 1)
+ {
utils().revertWithStringData(*arguments.at(1)->annotation().type);
+ // Here, the argument is consumed, but in the other branch, it is still there.
+ m_context.adjustStackOffset(arguments.at(1)->annotation().type->sizeOnStack());
+ }
else
m_context.appendRevert();
// the success branch
@@ -1706,13 +1710,23 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co
m_context.appendConditionalInvalid();
}
+ m_context << Instruction::SWAP1;
+ // stack: value_to_shift shift_amount
+
switch (_operator)
{
case Token::SHL:
- m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::MUL;
+ if (m_context.evmVersion().hasBitwiseShifting())
+ m_context << Instruction::SHL;
+ else
+ m_context << u256(2) << Instruction::EXP << Instruction::MUL;
break;
case Token::SAR:
- m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV);
+ // NOTE: SAR rounds differently than SDIV
+ if (m_context.evmVersion().hasBitwiseShifting() && !c_valueSigned)
+ m_context << Instruction::SHR;
+ else
+ m_context << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV);
break;
case Token::SHR:
default:
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index e19cf41e..77684683 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -267,7 +267,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
else if (m_dataType->category() == Type::Category::FixedBytes)
{
solAssert(_sourceType.category() == Type::Category::FixedBytes, "source not fixed bytes");
- CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes(), false);
+ CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes());
}
else
{
diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h
index f8b68362..c576f9de 100644
--- a/libsolidity/codegen/LValue.h
+++ b/libsolidity/codegen/LValue.h
@@ -49,6 +49,7 @@ protected:
m_context(_compilerContext), m_dataType(_dataType) {}
public:
+ virtual ~LValue() {}
/// @returns the number of stack slots occupied by the lvalue reference
virtual unsigned sizeOnStack() const { return 1; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 777e57c3..425c5c1e 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -58,6 +58,19 @@ void SMTChecker::analyze(SourceUnit const& _source)
_source.accept(*this);
}
+bool SMTChecker::visit(ContractDefinition const& _contract)
+{
+ for (auto _var : _contract.stateVariables())
+ if (_var->type()->isValueType())
+ createVariable(*_var);
+ return true;
+}
+
+void SMTChecker::endVisit(ContractDefinition const&)
+{
+ m_stateVariables.clear();
+}
+
void SMTChecker::endVisit(VariableDeclaration const& _varDecl)
{
if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value())
@@ -72,13 +85,13 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
"Assertion checker does not yet support constructors and functions with modifiers."
);
m_currentFunction = &_function;
- // We only handle local variables, so we clear at the beginning of the function.
- // If we add storage variables, those should be cleared differently.
m_interface->reset();
m_variables.clear();
+ m_variables.insert(m_stateVariables.begin(), m_stateVariables.end());
m_pathConditions.clear();
- m_conditionalExecutionHappened = false;
+ m_loopExecutionHappened = false;
initializeLocalVariables(_function);
+ resetStateVariables();
return true;
}
@@ -132,6 +145,7 @@ bool SMTChecker::visit(WhileStatement const& _node)
visitBranch(_node.body(), expr(_node.condition()));
}
+ m_loopExecutionHappened = true;
resetVariables(touchedVariables);
return false;
@@ -171,7 +185,7 @@ bool SMTChecker::visit(ForStatement const& _node)
m_interface->pop();
- m_conditionalExecutionHappened = true;
+ m_loopExecutionHappened = true;
std::swap(sequenceCountersStart, m_variables);
resetVariables(touchedVariables);
@@ -548,7 +562,6 @@ SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _s
if (_condition)
popPathCondition();
- m_conditionalExecutionHappened = true;
std::swap(m_variables, beforeVars);
return beforeVars;
@@ -586,15 +599,21 @@ void SMTChecker::checkCondition(
expressionsToEvaluate.emplace_back(currentValue(*var));
expressionNames.push_back(var->name());
}
+ for (auto const& var: m_stateVariables)
+ if (knownVariable(*var.first))
+ {
+ expressionsToEvaluate.emplace_back(currentValue(*var.first));
+ expressionNames.push_back(var.first->name());
+ }
}
smt::CheckResult result;
vector<string> values;
tie(result, values) = checkSatisfiableAndGenerateModel(expressionsToEvaluate);
- string conditionalComment;
- if (m_conditionalExecutionHappened)
- conditionalComment =
- "\nNote that some information is erased after conditional execution of parts of the code.\n"
+ string loopComment;
+ if (m_loopExecutionHappened)
+ loopComment =
+ "\nNote that some information is erased after the execution of loops.\n"
"You can re-introduce information using require().";
switch (result)
{
@@ -607,17 +626,18 @@ void SMTChecker::checkCondition(
message << " for:\n";
solAssert(values.size() == expressionNames.size(), "");
for (size_t i = 0; i < values.size(); ++i)
- message << " " << expressionNames.at(i) << " = " << values.at(i) << "\n";
+ if (expressionsToEvaluate.at(i).name != values.at(i))
+ message << " " << expressionNames.at(i) << " = " << values.at(i) << "\n";
}
else
message << ".";
- m_errorReporter.warning(_location, message.str() + conditionalComment);
+ m_errorReporter.warning(_location, message.str() + loopComment);
break;
}
case smt::CheckResult::UNSATISFIABLE:
break;
case smt::CheckResult::UNKNOWN:
- m_errorReporter.warning(_location, _description + " might happen here." + conditionalComment);
+ m_errorReporter.warning(_location, _description + " might happen here." + loopComment);
break;
case smt::CheckResult::ERROR:
m_errorReporter.warning(_location, "Error trying to invoke SMT solver.");
@@ -722,6 +742,15 @@ void SMTChecker::initializeLocalVariables(FunctionDefinition const& _function)
setZeroValue(*retParam);
}
+void SMTChecker::resetStateVariables()
+{
+ for (auto const& variable: m_stateVariables)
+ {
+ newValue(*variable.first);
+ setUnknownValue(*variable.first);
+ }
+}
+
void SMTChecker::resetVariables(vector<Declaration const*> _variables)
{
for (auto const* decl: _variables)
@@ -752,7 +781,14 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl)
if (SSAVariable::isSupportedType(_varDecl.type()->category()))
{
solAssert(m_variables.count(&_varDecl) == 0, "");
- m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface));
+ solAssert(m_stateVariables.count(&_varDecl) == 0, "");
+ if (_varDecl.isLocalVariable())
+ m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface));
+ else
+ {
+ solAssert(_varDecl.isStateVariable(), "");
+ m_stateVariables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface));
+ }
return true;
}
else
diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h
index 7e7996cf..50d40ab9 100644
--- a/libsolidity/formal/SMTChecker.h
+++ b/libsolidity/formal/SMTChecker.h
@@ -50,6 +50,8 @@ private:
// because the order of expression evaluation is undefined
// TODO: or just force a certain order, but people might have a different idea about that.
+ virtual bool visit(ContractDefinition const& _node) override;
+ virtual void endVisit(ContractDefinition const& _node) override;
virtual void endVisit(VariableDeclaration const& _node) override;
virtual bool visit(FunctionDefinition const& _node) override;
virtual void endVisit(FunctionDefinition const& _node) override;
@@ -111,6 +113,7 @@ private:
smt::CheckResult checkSatisfiable();
void initializeLocalVariables(FunctionDefinition const& _function);
+ void resetStateVariables();
void resetVariables(std::vector<Declaration const*> _variables);
/// Given two different branches and the touched variables,
/// merge the touched variables into after-branch ite variables
@@ -160,9 +163,10 @@ private:
std::shared_ptr<smt::SolverInterface> m_interface;
std::shared_ptr<VariableUsage> m_variableUsage;
- bool m_conditionalExecutionHappened = false;
+ bool m_loopExecutionHappened = false;
std::map<Expression const*, smt::Expression> m_expressions;
std::map<Declaration const*, SSAVariable> m_variables;
+ std::map<Declaration const*, SSAVariable> m_stateVariables;
std::vector<smt::Expression> m_pathConditions;
ErrorReporter& m_errorReporter;
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index e127bb55..16796684 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -193,6 +193,7 @@ DEV_SIMPLE_EXCEPTION(SolverError);
class SolverInterface
{
public:
+ virtual ~SolverInterface() = default;
virtual void reset() = 0;
virtual void push() = 0;
diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h
index e4e4ea8d..e29ded26 100644
--- a/libsolidity/formal/SymbolicVariable.h
+++ b/libsolidity/formal/SymbolicVariable.h
@@ -40,6 +40,7 @@ public:
Declaration const& _decl,
smt::SolverInterface& _interface
);
+ virtual ~SymbolicVariable() = default;
smt::Expression operator()(int _seq) const
{
diff --git a/libsolidity/formal/VariableUsage.cpp b/libsolidity/formal/VariableUsage.cpp
index 4e96059d..c2dea844 100644
--- a/libsolidity/formal/VariableUsage.cpp
+++ b/libsolidity/formal/VariableUsage.cpp
@@ -33,7 +33,6 @@ VariableUsage::VariableUsage(ASTNode const& _node)
solAssert(declaration, "");
if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
if (
- varDecl->isLocalVariable() &&
identifier->annotation().lValueRequested &&
varDecl->annotation().type->isValueType()
)
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index abf7ddf2..9f505889 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -54,6 +54,7 @@ bool AsmAnalyzer::analyze(Block const& _block)
bool AsmAnalyzer::operator()(Label const& _label)
{
+ solAssert(!_label.name.empty(), "");
checkLooseFeature(
_label.location,
"The use of labels is deprecated. Please use \"if\", \"switch\", \"for\" or function calls instead."
@@ -107,6 +108,7 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal)
bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
{
+ solAssert(!_identifier.name.empty(), "");
size_t numErrorsBefore = m_errorReporter.errors().size();
bool success = true;
if (m_currentScope->lookup(_identifier.name, Scope::Visitor(
@@ -208,6 +210,7 @@ bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
bool AsmAnalyzer::operator()(assembly::Assignment const& _assignment)
{
+ solAssert(_assignment.value, "");
int const expectedItems = _assignment.variableNames.size();
solAssert(expectedItems >= 1, "");
int const stackHeight = m_stackHeight;
@@ -259,6 +262,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl)
bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
{
+ solAssert(!_funDef.name.empty(), "");
Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
solAssert(virtualBlock, "");
Scope& varScope = scope(virtualBlock);
@@ -280,6 +284,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall)
{
+ solAssert(!_funCall.functionName.name.empty(), "");
bool success = true;
size_t arguments = 0;
size_t returns = 0;
@@ -349,6 +354,8 @@ bool AsmAnalyzer::operator()(If const& _if)
bool AsmAnalyzer::operator()(Switch const& _switch)
{
+ solAssert(_switch.expression, "");
+
bool success = true;
if (!expectExpression(*_switch.expression))
@@ -391,6 +398,8 @@ bool AsmAnalyzer::operator()(Switch const& _switch)
bool AsmAnalyzer::operator()(assembly::ForLoop const& _for)
{
+ solAssert(_for.condition, "");
+
Scope* originalScope = m_currentScope;
bool success = true;
@@ -478,6 +487,7 @@ bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation con
bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t _valueSize)
{
+ solAssert(!_variable.name.empty(), "");
bool success = true;
size_t numErrorsBefore = m_errorReporter.errors().size();
size_t variableSize(-1);
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 7f618e07..d3b0808b 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -276,7 +276,7 @@ assembly::Expression Parser::parseExpression()
int args = instructionInfo(instr.instruction).args;
if (args > 0 && currentToken() != Token::LParen)
fatalParserError(string(
- "Expected token \"(\" (\"" +
+ "Expected '(' (instruction \"" +
instructionNames().at(instr.instruction) +
"\" expects " +
boost::lexical_cast<string>(args) +
@@ -504,7 +504,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
/// check for premature closing parentheses
if (currentToken() == Token::RParen)
fatalParserError(string(
- "Expected expression (\"" +
+ "Expected expression (instruction \"" +
instructionNames().at(instr) +
"\" expects " +
boost::lexical_cast<string>(args) +
@@ -516,7 +516,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
{
if (currentToken() != Token::Comma)
fatalParserError(string(
- "Expected comma (\"" +
+ "Expected ',' (instruction \"" +
instructionNames().at(instr) +
"\" expects " +
boost::lexical_cast<string>(args) +
@@ -529,7 +529,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
ret.location.end = endPosition();
if (currentToken() == Token::Comma)
fatalParserError(string(
- "Expected ')' (\"" +
+ "Expected ')' (instruction \"" +
instructionNames().at(instr) +
"\" expects " +
boost::lexical_cast<string>(args) +
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 4ff14aa2..47dc30cf 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -29,6 +29,8 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/ControlFlowAnalyzer.h>
+#include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/TypeChecker.h>
@@ -224,6 +226,22 @@ bool CompilerStack::analyze()
if (noErrors)
{
+ CFG cfg(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!cfg.constructFlow(*source->ast))
+ noErrors = false;
+
+ if (noErrors)
+ {
+ ControlFlowAnalyzer controlFlowAnalyzer(cfg, m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!controlFlowAnalyzer.analyze(*source->ast))
+ noErrors = false;
+ }
+ }
+
+ if (noErrors)
+ {
StaticAnalyzer staticAnalyzer(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!staticAnalyzer.analyze(*source->ast))
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index a496cc21..a532f86e 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -136,13 +136,22 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
ExpressionClasses& classes = state->expressionClasses();
using Id = ExpressionClasses::Id;
using Ids = vector<Id>;
- // div(calldataload(0), 1 << 224) equals to hashValue
Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(_signature)))));
Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
- classes.forceEqual(hashValue, Instruction::DIV, Ids{
- calldata,
- classes.find(u256(1) << 224)
- });
+ if (!m_evmVersion.hasBitwiseShifting())
+ // div(calldataload(0), 1 << 224) equals to hashValue
+ classes.forceEqual(
+ hashValue,
+ Instruction::DIV,
+ Ids{calldata, classes.find(u256(1) << 224)}
+ );
+ else
+ // shr(0xe0, calldataload(0)) equals to hashValue
+ classes.forceEqual(
+ hashValue,
+ Instruction::SHR,
+ Ids{classes.find(u256(0xe0)), calldata}
+ );
// lt(calldatasize(), 4) equals to 0 (ignore the shortcut for fallback functions)
classes.forceEqual(
classes.find(u256(0)),
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
index d058d556..d9588e5c 100644
--- a/libsolidity/parsing/DocStringParser.cpp
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -72,7 +72,7 @@ bool DocStringParser::parse(string const& _docString, ErrorReporter& _errorRepor
auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end);
if (tagNameEndPos == end)
{
- appendError("End of tag " + string(tagPos, tagNameEndPos) + "not found");
+ appendError("End of tag " + string(tagPos, tagNameEndPos) + " not found");
break;
}
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 618a0896..e2e1eebc 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -54,6 +54,7 @@ public:
template <class NodeType, typename... Args>
ASTPointer<NodeType> createNode(Args&& ... _args)
{
+ solAssert(m_location.sourceName, "");
if (m_location.end < 0)
markEndPosition();
return make_shared<NodeType>(m_location, forward<Args>(_args)...);
@@ -528,7 +529,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
break;
expectToken(Token::Comma);
if (m_scanner->currentToken() != Token::Identifier)
- fatalParserError(string("Expected Identifier after ','"));
+ fatalParserError(string("Expected identifier after ','"));
}
if (members.size() == 0)
parserError({"enum with no members is not allowed."});
@@ -1057,16 +1058,16 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
if (m_scanner->currentToken() != Token::Identifier)
fatalParserError("Expected event name or path.");
- vector<ASTPointer<PrimaryExpression>> path;
+ IndexAccessedPath iap;
while (true)
{
- path.push_back(parseIdentifier());
+ iap.path.push_back(parseIdentifier());
if (m_scanner->currentToken() != Token::Period)
break;
m_scanner->next();
};
- auto eventName = expressionFromIndexAccessStructure(path, {});
+ auto eventName = expressionFromIndexAccessStructure(iap);
expectToken(Token::LParen);
vector<ASTPointer<Expression>> arguments;
@@ -1083,61 +1084,124 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString)
{
RecursionGuard recursionGuard(*this);
+ LookAheadInfo statementType;
+ IndexAccessedPath iap;
+
+ if (m_scanner->currentToken() == Token::LParen)
+ {
+ ASTNodeFactory nodeFactory(*this);
+ size_t emptyComponents = 0;
+ // First consume all empty components.
+ expectToken(Token::LParen);
+ while (m_scanner->currentToken() == Token::Comma)
+ {
+ m_scanner->next();
+ emptyComponents++;
+ }
+
+ // Now see whether we have a variable declaration or an expression.
+ tie(statementType, iap) = tryParseIndexAccessedPath();
+ switch (statementType)
+ {
+ case LookAheadInfo::VariableDeclaration:
+ {
+ vector<ASTPointer<VariableDeclaration>> variables;
+ ASTPointer<Expression> value;
+ // We have already parsed something like `(,,,,a.b.c[2][3]`
+ VarDeclParserOptions options;
+ options.allowLocationSpecifier = true;
+ variables = vector<ASTPointer<VariableDeclaration>>(emptyComponents, nullptr);
+ variables.push_back(parseVariableDeclaration(options, typeNameFromIndexAccessStructure(iap)));
+
+ while (m_scanner->currentToken() != Token::RParen)
+ {
+ expectToken(Token::Comma);
+ if (m_scanner->currentToken() == Token::Comma || m_scanner->currentToken() == Token::RParen)
+ variables.push_back(nullptr);
+ else
+ variables.push_back(parseVariableDeclaration(options));
+ }
+ expectToken(Token::RParen);
+ expectToken(Token::Assign);
+ value = parseExpression();
+ nodeFactory.setEndPositionFromNode(value);
+ return nodeFactory.createNode<VariableDeclarationStatement>(_docString, variables, value);
+ }
+ case LookAheadInfo::Expression:
+ {
+ // Complete parsing the expression in the current component.
+ vector<ASTPointer<Expression>> components(emptyComponents, nullptr);
+ components.push_back(parseExpression(expressionFromIndexAccessStructure(iap)));
+ while (m_scanner->currentToken() != Token::RParen)
+ {
+ expectToken(Token::Comma);
+ if (m_scanner->currentToken() == Token::Comma || m_scanner->currentToken() == Token::RParen)
+ components.push_back(ASTPointer<Expression>());
+ else
+ components.push_back(parseExpression());
+ }
+ nodeFactory.markEndPosition();
+ expectToken(Token::RParen);
+ return parseExpressionStatement(_docString, nodeFactory.createNode<TupleExpression>(components, false));
+ }
+ default:
+ solAssert(false, "");
+ }
+ }
+ else
+ {
+ tie(statementType, iap) = tryParseIndexAccessedPath();
+ switch (statementType)
+ {
+ case LookAheadInfo::VariableDeclaration:
+ return parseVariableDeclarationStatement(_docString, typeNameFromIndexAccessStructure(iap));
+ case LookAheadInfo::Expression:
+ return parseExpressionStatement(_docString, expressionFromIndexAccessStructure(iap));
+ default:
+ solAssert(false, "");
+ }
+ }
+}
+
+bool Parser::IndexAccessedPath::empty() const
+{
+ if (!indices.empty())
+ {
+ solAssert(!path.empty(), "");
+ }
+ return path.empty() && indices.empty();
+}
+
+
+pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAccessedPath()
+{
// These two cases are very hard to distinguish:
- // x[7 * 20 + 3] a; - x[7 * 20 + 3] = 9;
+ // x[7 * 20 + 3] a; and x[7 * 20 + 3] = 9;
// In the first case, x is a type name, in the second it is the name of a variable.
// As an extension, we can even have:
// `x.y.z[1][2] a;` and `x.y.z[1][2] = 10;`
// Where in the first, x.y.z leads to a type name where in the second, it accesses structs.
- switch (peekStatementType())
+
+ auto statementType = peekStatementType();
+ switch (statementType)
{
- case LookAheadInfo::VariableDeclarationStatement:
- return parseVariableDeclarationStatement(_docString);
- case LookAheadInfo::ExpressionStatement:
- return parseExpressionStatement(_docString);
+ case LookAheadInfo::VariableDeclaration:
+ case LookAheadInfo::Expression:
+ return make_pair(statementType, IndexAccessedPath());
default:
break;
}
+
// At this point, we have 'Identifier "["' or 'Identifier "." Identifier' or 'ElementoryTypeName "["'.
- // We parse '(Identifier ("." Identifier)* |ElementaryTypeName) ( "[" Expression "]" )+'
+ // We parse '(Identifier ("." Identifier)* |ElementaryTypeName) ( "[" Expression "]" )*'
// until we can decide whether to hand this over to ExpressionStatement or create a
// VariableDeclarationStatement out of it.
- vector<ASTPointer<PrimaryExpression>> path;
- bool startedWithElementary = false;
- if (m_scanner->currentToken() == Token::Identifier)
- path.push_back(parseIdentifier());
- else
- {
- startedWithElementary = true;
- unsigned firstNum;
- unsigned secondNum;
- tie(firstNum, secondNum) = m_scanner->currentTokenInfo();
- ElementaryTypeNameToken elemToken(m_scanner->currentToken(), firstNum, secondNum);
- path.push_back(ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(elemToken));
- m_scanner->next();
- }
- while (!startedWithElementary && m_scanner->currentToken() == Token::Period)
- {
- m_scanner->next();
- path.push_back(parseIdentifier());
- }
- vector<pair<ASTPointer<Expression>, SourceLocation>> indices;
- while (m_scanner->currentToken() == Token::LBrack)
- {
- expectToken(Token::LBrack);
- ASTPointer<Expression> index;
- if (m_scanner->currentToken() != Token::RBrack)
- index = parseExpression();
- SourceLocation indexLocation = path.front()->location();
- indexLocation.end = endPosition();
- indices.push_back(make_pair(index, indexLocation));
- expectToken(Token::RBrack);
- }
+ IndexAccessedPath iap = parseIndexAccessedPath();
if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken()))
- return parseVariableDeclarationStatement(_docString, typeNameIndexAccessStructure(path, indices));
+ return make_pair(LookAheadInfo::VariableDeclaration, move(iap));
else
- return parseExpressionStatement(_docString, expressionFromIndexAccessStructure(path, indices));
+ return make_pair(LookAheadInfo::Expression, move(iap));
}
ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStatement(
@@ -1145,6 +1209,9 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTPointer<TypeName> const& _lookAheadArrayType
)
{
+ // This does not parse multi variable declaration statements starting directly with
+ // `(`, they are parsed in parseSimpleStatement, because they are hard to distinguish
+ // from tuple expressions.
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
if (_lookAheadArrayType)
@@ -1207,23 +1274,24 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTPointer<ExpressionStatement> Parser::parseExpressionStatement(
ASTPointer<ASTString> const& _docString,
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+ ASTPointer<Expression> const& _partialParserResult
)
{
RecursionGuard recursionGuard(*this);
- ASTPointer<Expression> expression = parseExpression(_lookAheadIndexAccessStructure);
+ ASTPointer<Expression> expression = parseExpression(_partialParserResult);
return ASTNodeFactory(*this, expression).createNode<ExpressionStatement>(_docString, expression);
}
ASTPointer<Expression> Parser::parseExpression(
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+ ASTPointer<Expression> const& _partiallyParsedExpression
)
{
RecursionGuard recursionGuard(*this);
- ASTPointer<Expression> expression = parseBinaryExpression(4, _lookAheadIndexAccessStructure);
+ ASTPointer<Expression> expression = parseBinaryExpression(4, _partiallyParsedExpression);
if (Token::isAssignmentOp(m_scanner->currentToken()))
{
- Token::Value assignmentOperator = expectAssignmentOperator();
+ Token::Value assignmentOperator = m_scanner->currentToken();
+ m_scanner->next();
ASTPointer<Expression> rightHandSide = parseExpression();
ASTNodeFactory nodeFactory(*this, expression);
nodeFactory.setEndPositionFromNode(rightHandSide);
@@ -1245,11 +1313,11 @@ ASTPointer<Expression> Parser::parseExpression(
ASTPointer<Expression> Parser::parseBinaryExpression(
int _minPrecedence,
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+ ASTPointer<Expression> const& _partiallyParsedExpression
)
{
RecursionGuard recursionGuard(*this);
- ASTPointer<Expression> expression = parseUnaryExpression(_lookAheadIndexAccessStructure);
+ ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression);
ASTNodeFactory nodeFactory(*this, expression);
int precedence = Token::precedence(m_scanner->currentToken());
for (; precedence >= _minPrecedence; --precedence)
@@ -1265,14 +1333,14 @@ ASTPointer<Expression> Parser::parseBinaryExpression(
}
ASTPointer<Expression> Parser::parseUnaryExpression(
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+ ASTPointer<Expression> const& _partiallyParsedExpression
)
{
RecursionGuard recursionGuard(*this);
- ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
- ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
+ ASTNodeFactory nodeFactory = _partiallyParsedExpression ?
+ ASTNodeFactory(*this, _partiallyParsedExpression) : ASTNodeFactory(*this);
Token::Value token = m_scanner->currentToken();
- if (!_lookAheadIndexAccessStructure && (Token::isUnaryOp(token) || Token::isCountOp(token)))
+ if (!_partiallyParsedExpression && (Token::isUnaryOp(token) || Token::isCountOp(token)))
{
// prefix expression
m_scanner->next();
@@ -1283,7 +1351,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
else
{
// potential postfix expression
- ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_lookAheadIndexAccessStructure);
+ ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_partiallyParsedExpression);
token = m_scanner->currentToken();
if (!Token::isCountOp(token))
return subExpression;
@@ -1294,16 +1362,16 @@ ASTPointer<Expression> Parser::parseUnaryExpression(
}
ASTPointer<Expression> Parser::parseLeftHandSideExpression(
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure
+ ASTPointer<Expression> const& _partiallyParsedExpression
)
{
RecursionGuard recursionGuard(*this);
- ASTNodeFactory nodeFactory = _lookAheadIndexAccessStructure ?
- ASTNodeFactory(*this, _lookAheadIndexAccessStructure) : ASTNodeFactory(*this);
+ ASTNodeFactory nodeFactory = _partiallyParsedExpression ?
+ ASTNodeFactory(*this, _partiallyParsedExpression) : ASTNodeFactory(*this);
ASTPointer<Expression> expression;
- if (_lookAheadIndexAccessStructure)
- expression = _lookAheadIndexAccessStructure;
+ if (_partiallyParsedExpression)
+ expression = _partiallyParsedExpression;
else if (m_scanner->currentToken() == Token::New)
{
expectToken(Token::New);
@@ -1517,44 +1585,79 @@ Parser::LookAheadInfo Parser::peekStatementType() const
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
if (token == Token::Mapping || token == Token::Function || token == Token::Var)
- return LookAheadInfo::VariableDeclarationStatement;
+ return LookAheadInfo::VariableDeclaration;
if (mightBeTypeName)
{
Token::Value next = m_scanner->peekNextToken();
if (next == Token::Identifier || Token::isLocationSpecifier(next))
- return LookAheadInfo::VariableDeclarationStatement;
+ return LookAheadInfo::VariableDeclaration;
if (next == Token::LBrack || next == Token::Period)
return LookAheadInfo::IndexAccessStructure;
}
- return LookAheadInfo::ExpressionStatement;
+ return LookAheadInfo::Expression;
}
-ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
- vector<ASTPointer<PrimaryExpression>> const& _path,
- vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
-)
+Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
{
- solAssert(!_path.empty(), "");
+ IndexAccessedPath iap;
+ if (m_scanner->currentToken() == Token::Identifier)
+ {
+ iap.path.push_back(parseIdentifier());
+ while (m_scanner->currentToken() == Token::Period)
+ {
+ m_scanner->next();
+ iap.path.push_back(parseIdentifier());
+ }
+ }
+ else
+ {
+ unsigned firstNum;
+ unsigned secondNum;
+ tie(firstNum, secondNum) = m_scanner->currentTokenInfo();
+ ElementaryTypeNameToken elemToken(m_scanner->currentToken(), firstNum, secondNum);
+ iap.path.push_back(ASTNodeFactory(*this).createNode<ElementaryTypeNameExpression>(elemToken));
+ m_scanner->next();
+ }
+ while (m_scanner->currentToken() == Token::LBrack)
+ {
+ expectToken(Token::LBrack);
+ ASTPointer<Expression> index;
+ if (m_scanner->currentToken() != Token::RBrack)
+ index = parseExpression();
+ SourceLocation indexLocation = iap.path.front()->location();
+ indexLocation.end = endPosition();
+ iap.indices.push_back(make_pair(index, indexLocation));
+ expectToken(Token::RBrack);
+ }
+
+ return iap;
+}
+
+ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAccessedPath const& _iap)
+{
+ if (_iap.empty())
+ return {};
+
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
- SourceLocation location = _path.front()->location();
- location.end = _path.back()->location().end;
+ SourceLocation location = _iap.path.front()->location();
+ location.end = _iap.path.back()->location().end;
nodeFactory.setLocation(location);
ASTPointer<TypeName> type;
- if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_path.front().get()))
+ if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_iap.path.front().get()))
{
- solAssert(_path.size() == 1, "");
+ solAssert(_iap.path.size() == 1, "");
type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeName());
}
else
{
vector<ASTString> path;
- for (auto const& el: _path)
+ for (auto const& el: _iap.path)
path.push_back(dynamic_cast<Identifier const&>(*el).name());
type = nodeFactory.createNode<UserDefinedTypeName>(path);
}
- for (auto const& lengthExpression: _indices)
+ for (auto const& lengthExpression: _iap.indices)
{
nodeFactory.setLocation(lengthExpression.second);
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.first);
@@ -1563,26 +1666,27 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
}
ASTPointer<Expression> Parser::expressionFromIndexAccessStructure(
- vector<ASTPointer<PrimaryExpression>> const& _path,
- vector<pair<ASTPointer<Expression>, SourceLocation>> const& _indices
+ Parser::IndexAccessedPath const& _iap
)
{
- solAssert(!_path.empty(), "");
+ if (_iap.empty())
+ return {};
+
RecursionGuard recursionGuard(*this);
- ASTNodeFactory nodeFactory(*this, _path.front());
- ASTPointer<Expression> expression(_path.front());
- for (size_t i = 1; i < _path.size(); ++i)
+ ASTNodeFactory nodeFactory(*this, _iap.path.front());
+ ASTPointer<Expression> expression(_iap.path.front());
+ for (size_t i = 1; i < _iap.path.size(); ++i)
{
- SourceLocation location(_path.front()->location());
- location.end = _path[i]->location().end;
+ SourceLocation location(_iap.path.front()->location());
+ location.end = _iap.path[i]->location().end;
nodeFactory.setLocation(location);
- Identifier const& identifier = dynamic_cast<Identifier const&>(*_path[i]);
+ Identifier const& identifier = dynamic_cast<Identifier const&>(*_iap.path[i]);
expression = nodeFactory.createNode<MemberAccess>(
expression,
make_shared<ASTString>(identifier.name())
);
}
- for (auto const& index: _indices)
+ for (auto const& index: _iap.indices)
{
nodeFactory.setLocation(index.second);
expression = nodeFactory.createNode<IndexAccess>(expression, index.first);
@@ -1598,40 +1702,10 @@ ASTPointer<ParameterList> Parser::createEmptyParameterList()
return nodeFactory.createNode<ParameterList>(vector<ASTPointer<VariableDeclaration>>());
}
-string Parser::currentTokenName()
-{
- Token::Value token = m_scanner->currentToken();
- if (Token::isElementaryTypeName(token)) //for the sake of accuracy in reporting
- {
- ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
- return elemTypeName.toString();
- }
- else
- return Token::name(token);
-}
-
-Token::Value Parser::expectAssignmentOperator()
-{
- Token::Value op = m_scanner->currentToken();
- if (!Token::isAssignmentOp(op))
- fatalParserError(
- string("Expected assignment operator, got '") +
- currentTokenName() +
- string("'")
- );
- m_scanner->next();
- return op;
-}
-
ASTPointer<ASTString> Parser::expectIdentifierToken()
{
- Token::Value id = m_scanner->currentToken();
- if (id != Token::Identifier)
- fatalParserError(
- string("Expected identifier, got '") +
- currentTokenName() +
- string("'")
- );
+ // do not advance on success
+ expectToken(Token::Identifier, false);
return getLiteralAndAdvance();
}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index eb120a61..08653364 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -118,19 +118,19 @@ private:
);
ASTPointer<ExpressionStatement> parseExpressionStatement(
ASTPointer<ASTString> const& _docString,
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
);
ASTPointer<Expression> parseExpression(
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
);
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4,
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
);
ASTPointer<Expression> parseUnaryExpression(
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
);
ASTPointer<Expression> parseLeftHandSideExpression(
- ASTPointer<Expression> const& _lookAheadIndexAccessStructure = ASTPointer<Expression>()
+ ASTPointer<Expression> const& _partiallyParsedExpression = ASTPointer<Expression>()
);
ASTPointer<Expression> parsePrimaryExpression();
std::vector<ASTPointer<Expression>> parseFunctionCallListArguments();
@@ -143,26 +143,32 @@ private:
/// Used as return value of @see peekStatementType.
enum class LookAheadInfo
{
- IndexAccessStructure, VariableDeclarationStatement, ExpressionStatement
+ IndexAccessStructure, VariableDeclaration, Expression
+ };
+ /// Structure that represents a.b.c[x][y][z]. Can be converted either to an expression
+ /// or to a type name. For this to be valid, path cannot be empty, but indices can be empty.
+ struct IndexAccessedPath
+ {
+ std::vector<ASTPointer<PrimaryExpression>> path;
+ std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> indices;
+ bool empty() const;
};
+ std::pair<LookAheadInfo, IndexAccessedPath> tryParseIndexAccessedPath();
/// Performs limited look-ahead to distinguish between variable declaration and expression statement.
/// For source code of the form "a[][8]" ("IndexAccessStructure"), this is not possible to
/// decide with constant look-ahead.
LookAheadInfo peekStatementType() const;
- /// @returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]".
- ASTPointer<TypeName> typeNameIndexAccessStructure(
- std::vector<ASTPointer<PrimaryExpression>> const& _path,
- std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
- );
- /// @returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]".
- ASTPointer<Expression> expressionFromIndexAccessStructure(
- std::vector<ASTPointer<PrimaryExpression>> const& _path,
- std::vector<std::pair<ASTPointer<Expression>, SourceLocation>> const& _indices
- );
+ /// @returns an IndexAccessedPath as a prestage to parsing a variable declaration (type name)
+ /// or an expression;
+ IndexAccessedPath parseIndexAccessedPath();
+ /// @returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]",
+ /// or an empty pointer if an empty @a _pathAndIncides has been supplied.
+ ASTPointer<TypeName> typeNameFromIndexAccessStructure(IndexAccessedPath const& _pathAndIndices);
+ /// @returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]",
+ /// or an empty pointer if an empty @a _pathAndIncides has been supplied.
+ ASTPointer<Expression> expressionFromIndexAccessStructure(IndexAccessedPath const& _pathAndIndices);
- std::string currentTokenName();
- Token::Value expectAssignmentOperator();
ASTPointer<ASTString> expectIdentifierToken();
ASTPointer<ASTString> getLiteralAndAdvance();
///@}
diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp
index 5b83c5bd..71133746 100644
--- a/libsolidity/parsing/ParserBase.cpp
+++ b/libsolidity/parsing/ParserBase.cpp
@@ -63,42 +63,32 @@ Token::Value ParserBase::advance()
return m_scanner->next();
}
-void ParserBase::expectToken(Token::Value _value)
+void ParserBase::expectToken(Token::Value _value, bool _advance)
{
Token::Value tok = m_scanner->currentToken();
if (tok != _value)
{
- if (Token::isReservedKeyword(tok))
+ auto tokenName = [this](Token::Value _token)
{
- fatalParserError(
- string("Expected token ") +
- string(Token::name(_value)) +
- string(" got reserved keyword '") +
- string(Token::name(tok)) +
- string("'")
- );
- }
- else if (Token::isElementaryTypeName(tok)) //for the sake of accuracy in reporting
- {
- ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
- fatalParserError(
- string("Expected token ") +
- string(Token::name(_value)) +
- string(" got '") +
- elemTypeName.toString() +
- string("'")
- );
- }
- else
- fatalParserError(
- string("Expected token ") +
- string(Token::name(_value)) +
- string(" got '") +
- string(Token::name(m_scanner->currentToken())) +
- string("'")
- );
+ if (_token == Token::Identifier)
+ return string("identifier");
+ else if (_token == Token::EOS)
+ return string("end of source");
+ else if (Token::isReservedKeyword(_token))
+ return string("reserved keyword '") + Token::friendlyName(_token) + "'";
+ else if (Token::isElementaryTypeName(_token)) //for the sake of accuracy in reporting
+ {
+ ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken();
+ return string("'") + elemTypeName.toString() + "'";
+ }
+ else
+ return string("'") + Token::friendlyName(_token) + "'";
+ };
+
+ fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok));
}
- m_scanner->next();
+ if (_advance)
+ m_scanner->next();
}
void ParserBase::increaseRecursionDepth()
@@ -116,10 +106,10 @@ void ParserBase::decreaseRecursionDepth()
void ParserBase::parserError(string const& _description)
{
- m_errorReporter.parserError(SourceLocation(position(), position(), sourceName()), _description);
+ m_errorReporter.parserError(SourceLocation(position(), endPosition(), sourceName()), _description);
}
void ParserBase::fatalParserError(string const& _description)
{
- m_errorReporter.fatalParserError(SourceLocation(position(), position(), sourceName()), _description);
+ m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), sourceName()), _description);
}
diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h
index fd0de0d1..b28e1b1b 100644
--- a/libsolidity/parsing/ParserBase.h
+++ b/libsolidity/parsing/ParserBase.h
@@ -63,7 +63,7 @@ protected:
///@{
///@name Helper functions
/// If current token value is not _value, throw exception otherwise advance token.
- void expectToken(Token::Value _value);
+ void expectToken(Token::Value _value, bool _advance = true);
Token::Value currentToken() const;
Token::Value peekNextToken() const;
std::string currentLiteral() const;
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 805fbf5d..4d7a7bc6 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -304,6 +304,17 @@ public:
return m_string[tok];
}
+ static std::string friendlyName(Value tok)
+ {
+ char const* ret = toString(tok);
+ if (ret == nullptr)
+ {
+ ret = name(tok);
+ solAssert(ret != nullptr, "");
+ }
+ return std::string(ret);
+ }
+
// @returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise.
static int precedence(Value tok)
diff --git a/lllc/CMakeLists.txt b/lllc/CMakeLists.txt
index 5c480093..d6538ee2 100644
--- a/lllc/CMakeLists.txt
+++ b/lllc/CMakeLists.txt
@@ -1,5 +1,5 @@
add_executable(lllc main.cpp)
-target_link_libraries(lllc PRIVATE lll)
+target_link_libraries(lllc PRIVATE lll ${Boost_SYSTEM_LIBRARY})
if (INSTALL_LLLC)
include(GNUInstallDirs)
diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh
index 9e66799a..4e930707 100755
--- a/scripts/create_source_tarball.sh
+++ b/scripts/create_source_tarball.sh
@@ -32,7 +32,7 @@ REPO_ROOT="$(dirname "$0")"/..
fi
# Add dependencies
mkdir -p "$SOLDIR/deps/downloads/" 2>/dev/null || true
- wget -O "$SOLDIR/deps/downloads/jsoncpp-1.7.7.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.7.7.tar.gz
+ wget -O "$SOLDIR/deps/downloads/jsoncpp-1.8.4.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz
mkdir -p "$REPO_ROOT/upload"
tar czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring"
rm -r "$TEMPDIR"
diff --git a/scripts/install_cmake.sh b/scripts/install_cmake.sh
index 00d013b9..e334b2c9 100755
--- a/scripts/install_cmake.sh
+++ b/scripts/install_cmake.sh
@@ -18,6 +18,8 @@ esac
BIN=$PREFIX/bin
+PATH=$PREFIX/bin:$PATH
+
if test -f $BIN/cmake && ($BIN/cmake --version | grep -q "$VERSION"); then
echo "CMake $VERSION already installed in $BIN"
else
diff --git a/scripts/release.bat b/scripts/release.bat
index 54d85862..be95b35e 100644
--- a/scripts/release.bat
+++ b/scripts/release.bat
@@ -27,7 +27,19 @@ REM Copyright (c) 2016 solidity contributors.
REM ---------------------------------------------------------------------------
set CONFIGURATION=%1
+set VERSION=%2
+
+IF "%VERSION%"=="2015" (
+ set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll"
+) ELSE (
+
+ IF EXIST "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\" (
+ set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll"
+ ) ELSE (
+ set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\14.13.26020\x86\Microsoft.VC141.CRT\msvc*.dll"
+ )
+)
7z a solidity-windows.zip ^
.\build\solc\%CONFIGURATION%\solc.exe .\build\test\%CONFIGURATION%\soltest.exe ^
- "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll"
+ "%DLLS%"
diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh
index 375b4d1b..b1601336 100755
--- a/scripts/release_ppa.sh
+++ b/scripts/release_ppa.sh
@@ -76,7 +76,7 @@ mv solidity solc
# Fetch jsoncpp dependency
mkdir -p ./solc/deps/downloads/ 2>/dev/null || true
-wget -O ./solc/deps/downloads/jsoncpp-1.7.7.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.7.7.tar.gz
+wget -O ./solc/deps/downloads/jsoncpp-1.8.4.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.8.4.tar.gz
# Determine version
cd solc
diff --git a/scripts/tests.sh b/scripts/tests.sh
index 38073bf3..d63c1fe4 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -30,6 +30,17 @@ set -e
REPO_ROOT="$(dirname "$0")"/..
+IPC_ENABLED=true
+if [[ "$OSTYPE" == "darwin"* ]]
+then
+ SMT_FLAGS="--no-smt"
+ if [ "$CIRCLECI" ]
+ then
+ IPC_ENABLED=false
+ IPC_FLAGS="--no-ipc"
+ fi
+fi
+
if [ "$1" = --junit_report ]
then
if [ -z "$2" ]
@@ -98,8 +109,11 @@ function run_eth()
sleep 2
}
-download_eth
-ETH_PID=$(run_eth /tmp/test)
+if [ "$IPC_ENABLED" = true ];
+then
+ download_eth
+ ETH_PID=$(run_eth /tmp/test)
+fi
progress="--show-progress"
if [ "$CIRCLECI" ]
@@ -131,7 +145,7 @@ do
log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt
fi
fi
- "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc
+ "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" $SMT_FLAGS $IPC_FLAGS --ipcpath /tmp/test/geth.ipc
done
done
@@ -141,6 +155,9 @@ then
exit 1
fi
-pkill "$ETH_PID" || true
-sleep 4
-pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
+if [ "$IPC_ENABLED" = true ]
+then
+ pkill "$ETH_PID" || true
+ sleep 4
+ pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
+fi
diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh
index fd643429..f49ff5b2 100755
--- a/scripts/travis-emscripten/build_emscripten.sh
+++ b/scripts/travis-emscripten/build_emscripten.sh
@@ -40,6 +40,12 @@ if ! type git &>/dev/null; then
apt-get -y install git-core
fi
+if ! type wget &>/dev/null; then
+ # We need wget to install cmake
+ apt-get update
+ apt-get -y install wget
+fi
+
WORKSPACE=/root/project
# Increase nodejs stack size
@@ -67,6 +73,10 @@ rm -rf b2 libs doc tools more bin.v2 status
)
echo -en 'travis_fold:end:compiling_boost\\r'
+echo -en 'travis_fold:start:install_cmake.sh\\r'
+source $WORKSPACE/scripts/install_cmake.sh
+echo -en 'travis_fold:end:install_cmake.sh\\r'
+
# Build dependent components and solidity itself
echo -en 'travis_fold:start:compiling_solidity\\r'
cd $WORKSPACE
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index bd5e2eb1..1f04c68a 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -564,7 +564,8 @@ Allowed options)",
(
g_argOptimizeRuns.c_str(),
po::value<unsigned>()->value_name("n")->default_value(200),
- "Estimated number of contract runs for optimizer tuning."
+ "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_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.")
(
diff --git a/std/StandardToken.sol b/std/StandardToken.sol
index ca0658f2..c2fc3a66 100644
--- a/std/StandardToken.sol
+++ b/std/StandardToken.sol
@@ -13,11 +13,11 @@ contract StandardToken is Token {
balance[_initialOwner] = _supply;
}
- function balanceOf(address _account) constant public returns (uint) {
+ function balanceOf(address _account) view public returns (uint) {
return balance[_account];
}
- function totalSupply() constant public returns (uint) {
+ function totalSupply() view public returns (uint) {
return supply;
}
@@ -53,7 +53,7 @@ contract StandardToken is Token {
return true;
}
- function allowance(address _owner, address _spender) constant public returns (uint256) {
+ function allowance(address _owner, address _spender) view public returns (uint256) {
return m_allowance[_owner][_spender];
}
}
diff --git a/std/Token.sol b/std/Token.sol
index 4b4eb71e..7348a8f5 100644
--- a/std/Token.sol
+++ b/std/Token.sol
@@ -4,10 +4,10 @@ contract Token {
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
- function totalSupply() constant public returns (uint256 supply);
- function balanceOf(address _owner) constant public returns (uint256 balance);
+ function totalSupply() view public returns (uint256 supply);
+ function balanceOf(address _owner) view public returns (uint256 balance);
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
- function allowance(address _owner, address _spender) constant public returns (uint256 remaining);
+ function allowance(address _owner, address _spender) view public returns (uint256 remaining);
}
diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h
index ee8da322..4525cbf9 100644
--- a/test/ExecutionFramework.h
+++ b/test/ExecutionFramework.h
@@ -53,6 +53,7 @@ class ExecutionFramework
public:
ExecutionFramework();
+ virtual ~ExecutionFramework() = default;
virtual bytes const& compileAndRunWithoutCheck(
std::string const& _sourceCode,
diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp
index 089be45d..4b399a14 100644
--- a/test/libevmasm/Optimiser.cpp
+++ b/test/libevmasm/Optimiser.cpp
@@ -1072,6 +1072,75 @@ BOOST_AUTO_TEST_CASE(cse_sub_zero)
});
}
+BOOST_AUTO_TEST_CASE(cse_remove_unwanted_masking_of_address)
+{
+ vector<Instruction> ops{
+ Instruction::ADDRESS,
+ Instruction::CALLER,
+ Instruction::ORIGIN,
+ Instruction::COINBASE
+ };
+ for (auto const& op: ops)
+ {
+ checkCSE({
+ u256("0xffffffffffffffffffffffffffffffffffffffff"),
+ op,
+ Instruction::AND
+ }, {
+ op
+ });
+
+ checkCSE({
+ op,
+ u256("0xffffffffffffffffffffffffffffffffffffffff"),
+ Instruction::AND
+ }, {
+ op
+ });
+
+ // do not remove mask for other masking
+ checkCSE({
+ u256(1234),
+ op,
+ Instruction::AND
+ }, {
+ op,
+ u256(1234),
+ Instruction::AND
+ });
+
+ checkCSE({
+ op,
+ u256(1234),
+ Instruction::AND
+ }, {
+ u256(1234),
+ op,
+ Instruction::AND
+ });
+ }
+
+ // leave other opcodes untouched
+ checkCSE({
+ u256("0xffffffffffffffffffffffffffffffffffffffff"),
+ Instruction::CALLVALUE,
+ Instruction::AND
+ }, {
+ Instruction::CALLVALUE,
+ u256("0xffffffffffffffffffffffffffffffffffffffff"),
+ Instruction::AND
+ });
+
+ checkCSE({
+ Instruction::CALLVALUE,
+ u256("0xffffffffffffffffffffffffffffffffffffffff"),
+ Instruction::AND
+ }, {
+ u256("0xffffffffffffffffffffffffffffffffffffffff"),
+ Instruction::CALLVALUE,
+ Instruction::AND
+ });
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libjulia/Inliner.cpp b/test/libjulia/Inliner.cpp
index 88b51f28..464dcd93 100644
--- a/test/libjulia/Inliner.cpp
+++ b/test/libjulia/Inliner.cpp
@@ -1,18 +1,18 @@
/*
- This file is part of solidity.
+ 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 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.
+ 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 <http://www.gnu.org/licenses/>.
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @date 2017
@@ -23,6 +23,9 @@
#include <libjulia/optimiser/ExpressionInliner.h>
#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h>
+#include <libjulia/optimiser/FullInliner.h>
+#include <libjulia/optimiser/FunctionHoister.h>
+#include <libjulia/optimiser/FunctionGrouper.h>
#include <libsolidity/inlineasm/AsmPrinter.h>
@@ -58,8 +61,17 @@ string inlineFunctions(string const& _source, bool _julia = true)
ExpressionInliner(ast).run();
return assembly::AsmPrinter(_julia)(ast);
}
+string fullInline(string const& _source, bool _julia = true)
+{
+ Block ast = disambiguate(_source, _julia);
+ (FunctionHoister{})(ast);
+ (FunctionGrouper{})(ast);\
+ FullInliner(ast).run();
+ return assembly::AsmPrinter(_julia)(ast);
+}
}
+
BOOST_AUTO_TEST_SUITE(IuliaInlinableFunctionFilter)
BOOST_AUTO_TEST_CASE(smoke_test)
@@ -197,3 +209,135 @@ BOOST_AUTO_TEST_CASE(double_recursive_calls)
}
BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(IuliaFullInliner)
+
+BOOST_AUTO_TEST_CASE(simple)
+{
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a) -> x { let r := mul(a, a) x := add(r, r) }"
+ "let y := add(f(sload(mload(2))), mload(7))"
+ "}", false),
+ format("{"
+ "{"
+ "let _1 := mload(7)"
+ "let f_a := sload(mload(2))"
+ "let f_x"
+ "{"
+ "let f_r := mul(f_a, f_a)"
+ "f_x := add(f_r, f_r)"
+ "}"
+ "let y := add(f_x, _1)"
+ "}"
+ "function f(a) -> x"
+ "{"
+ "let r := mul(a, a)"
+ "x := add(r, r)"
+ "}"
+ "}", false)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(multi_fun)
+{
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a) -> x { x := add(a, a) }"
+ "function g(b, c) -> y { y := mul(mload(c), f(b)) }"
+ "let y := g(f(3), 7)"
+ "}", false),
+ format("{"
+ "{"
+ "let g_c := 7 "
+ "let f_a_1 := 3 "
+ "let f_x_1 "
+ "{ f_x_1 := add(f_a_1, f_a_1) } "
+ "let g_y "
+ "{"
+ "let g_f_a := f_x_1 "
+ "let g_f_x "
+ "{"
+ "g_f_x := add(g_f_a, g_f_a)"
+ "}"
+ "g_y := mul(mload(g_c), g_f_x)"
+ "}"
+ "let y_1 := g_y"
+ "}"
+ "function f(a) -> x"
+ "{"
+ "x := add(a, a)"
+ "}"
+ "function g(b, c) -> y"
+ "{"
+ "let f_a := b "
+ "let f_x "
+ "{"
+ "f_x := add(f_a, f_a)"
+ "}"
+ "y := mul(mload(c), f_x)"
+ "}"
+ "}", false)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(move_up_rightwards_arguments)
+{
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a, b, c) -> x { x := add(a, b) x := mul(x, c) }"
+ "let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5)))"
+ "}", false),
+ format("{"
+ "{"
+ "let _1 := mload(5)"
+ "let f_c := mload(4)"
+ "let f_b := mload(3)"
+ "let f_a := mload(2)"
+ "let f_x"
+ "{"
+ "f_x := add(f_a, f_b)"
+ "f_x := mul(f_x, f_c)"
+ "}"
+ "let y := add(mload(1), add(f_x, _1))"
+ "}"
+ "function f(a, b, c) -> x"
+ "{"
+ "x := add(a, b)"
+ "x := mul(x, c)"
+ "}"
+ "}", false)
+ );
+}
+
+BOOST_AUTO_TEST_CASE(pop_result)
+{
+ // This tests that `pop(r)` is removed.
+ BOOST_CHECK_EQUAL(
+ fullInline("{"
+ "function f(a) -> x { let r := mul(a, a) x := add(r, r) }"
+ "pop(add(f(7), 2))"
+ "}", false),
+ format("{"
+ "{"
+ "let _1 := 2 "
+ "let f_a := 7 "
+ "let f_x "
+ "{"
+ "let f_r := mul(f_a, f_a) "
+ "f_x := add(f_r, f_r)"
+ "}"
+ "{"
+ "}"
+ "}"
+ "function f(a) -> x"
+ "{"
+ "let r := mul(a, a) "
+ "x := add(r, r)"
+ "}"
+ "}", false)
+ );
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libjulia/MainFunction.cpp b/test/libjulia/MainFunction.cpp
new file mode 100644
index 00000000..c26b002d
--- /dev/null
+++ b/test/libjulia/MainFunction.cpp
@@ -0,0 +1,87 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @date 2018
+ * Unit tests for the Julia MainFunction transformation.
+ */
+
+#include <test/libjulia/Common.h>
+
+#include <libjulia/optimiser/FunctionGrouper.h>
+#include <libjulia/optimiser/MainFunction.h>
+
+#include <libsolidity/inlineasm/AsmPrinter.h>
+
+#include <boost/test/unit_test.hpp>
+
+using namespace std;
+using namespace dev::julia;
+using namespace dev::julia::test;
+using namespace dev::solidity;
+
+#define CHECK(_original, _expectation)\
+do\
+{\
+ assembly::AsmPrinter p(true);\
+ Block b = disambiguate(_original);\
+ (FunctionGrouper{})(b);\
+ (MainFunction{})(b);\
+ string result = p(b);\
+ BOOST_CHECK_EQUAL(result, format(_expectation));\
+}\
+while(false)
+
+BOOST_AUTO_TEST_SUITE(JuliaMainFunction)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ CHECK("{ }", "{ function main() { } }");
+}
+
+BOOST_AUTO_TEST_CASE(single_fun)
+{
+ CHECK(
+ "{ let a:u256 function f() {} }",
+ "{ function main() { let a:u256 } function f() {} }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(multi_fun_mixed)
+{
+ CHECK(
+ "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }",
+ "{ function main() { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(nested_fun)
+{
+ CHECK(
+ "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }",
+ "{ function main() { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(empty_block)
+{
+ CHECK(
+ "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }",
+ "{ function main() { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }"
+ );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp
index 9d66658e..96261dec 100644
--- a/test/libjulia/Parser.cpp
+++ b/test/libjulia/Parser.cpp
@@ -212,10 +212,10 @@ BOOST_AUTO_TEST_CASE(tokens_as_identifers)
BOOST_AUTO_TEST_CASE(lacking_types)
{
- CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'");
- CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected token Colon got 'RBrace'");
- CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected token Colon got 'RParen'");
- CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected token Colon got 'LBrace'");
+ CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected identifier but got '='");
+ CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'");
+ CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'");
+ CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'");
}
BOOST_AUTO_TEST_CASE(invalid_types)
@@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(if_statement)
BOOST_AUTO_TEST_CASE(if_statement_invalid)
{
CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected.");
- CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected token LBrace");
+ CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'");
// TODO change this to an error once we check types.
BOOST_CHECK(successParse("{ if 42:u256 { } }"));
}
diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h
index 05490a42..a904617d 100644
--- a/test/libsolidity/AnalysisFramework.h
+++ b/test/libsolidity/AnalysisFramework.h
@@ -52,6 +52,7 @@ protected:
bool _insertVersionPragma = true,
bool _allowMultipleErrors = false
);
+ virtual ~AnalysisFramework() = default;
SourceUnit const* parseAndAnalyse(std::string const& _source);
bool success(std::string const& _source);
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index 77ca363a..7b3df043 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -158,8 +158,9 @@ BOOST_AUTO_TEST_CASE(location_test)
}
)";
AssemblyItems items = compileContract(sourceCode);
+ bool hasShifts = dev::test::Options::get().evmVersion().hasBitwiseShifting();
vector<SourceLocation> locations =
- vector<SourceLocation>(24, SourceLocation(2, 75, make_shared<string>(""))) +
+ vector<SourceLocation>(hasShifts ? 23 : 24, SourceLocation(2, 75, make_shared<string>(""))) +
vector<SourceLocation>(2, SourceLocation(20, 72, make_shared<string>(""))) +
vector<SourceLocation>(1, SourceLocation(8, 17, make_shared<string>("--CODEGEN--"))) +
vector<SourceLocation>(3, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) +
@@ -172,8 +173,6 @@ BOOST_AUTO_TEST_CASE(location_test)
vector<SourceLocation>(1, SourceLocation(65, 67, make_shared<string>(""))) +
vector<SourceLocation>(2, SourceLocation(58, 67, make_shared<string>(""))) +
vector<SourceLocation>(2, SourceLocation(20, 72, make_shared<string>("")));
-
-
checkAssemblyLocations(items, locations);
}
diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp
index 0d66456c..f16d9abe 100644
--- a/test/libsolidity/GasMeter.cpp
+++ b/test/libsolidity/GasMeter.cpp
@@ -307,6 +307,46 @@ BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback)
testCreationTimeGas(sourceCode);
testRunTimeGas("x()", vector<bytes>{encodeArgs()});
}
+
+BOOST_AUTO_TEST_CASE(complex_control_flow)
+{
+ // This crashed the gas estimator previously (or took a very long time).
+ // Now we do not follow branches if they start out with lower gas costs than the ones
+ // we previously considered. This of course reduces accuracy.
+ char const* sourceCode = R"(
+ contract log {
+ function ln(int128 x) constant returns (int128 result) {
+ int128 t = x / 256;
+ int128 y = 5545177;
+ x = t;
+ t = x * 16; if (t <= 1000000) { x = t; y = y - 2772588; }
+ t = x * 4; if (t <= 1000000) { x = t; y = y - 1386294; }
+ t = x * 2; if (t <= 1000000) { x = t; y = y - 693147; }
+ t = x + x / 2; if (t <= 1000000) { x = t; y = y - 405465; }
+ t = x + x / 4; if (t <= 1000000) { x = t; y = y - 223144; }
+ t = x + x / 8; if (t <= 1000000) { x = t; y = y - 117783; }
+ t = x + x / 16; if (t <= 1000000) { x = t; y = y - 60624; }
+ t = x + x / 32; if (t <= 1000000) { x = t; y = y - 30771; }
+ t = x + x / 64; if (t <= 1000000) { x = t; y = y - 15504; }
+ t = x + x / 128; if (t <= 1000000) { x = t; y = y - 7782; }
+ t = x + x / 256; if (t <= 1000000) { x = t; y = y - 3898; }
+ t = x + x / 512; if (t <= 1000000) { x = t; y = y - 1951; }
+ t = x + x / 1024; if (t <= 1000000) { x = t; y = y - 976; }
+ t = x + x / 2048; if (t <= 1000000) { x = t; y = y - 488; }
+ t = x + x / 4096; if (t <= 1000000) { x = t; y = y - 244; }
+ t = x + x / 8192; if (t <= 1000000) { x = t; y = y - 122; }
+ t = x + x / 16384; if (t <= 1000000) { x = t; y = y - 61; }
+ t = x + x / 32768; if (t <= 1000000) { x = t; y = y - 31; }
+ t = x + x / 65536; if (t <= 1000000) { y = y - 15; }
+ return y;
+ }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+ // max gas is used for small x
+ testRunTimeGas("ln(int128)", vector<bytes>{encodeArgs(0), encodeArgs(10), encodeArgs(105), encodeArgs(30000)});
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index 0ced1792..181ca959 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
BOOST_AUTO_TEST_CASE(surplus_input)
{
- CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected token EOS");
+ CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected end of source but got '{'");
}
BOOST_AUTO_TEST_CASE(simple_instructions)
@@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE(functional)
BOOST_AUTO_TEST_CASE(functional_partial)
{
- CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected token \"(\"");
+ CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' (instruction \"byte\" expects 2 arguments)");
}
BOOST_AUTO_TEST_CASE(functional_partial_success)
@@ -290,10 +290,10 @@ BOOST_AUTO_TEST_CASE(if_statement_scope)
BOOST_AUTO_TEST_CASE(if_statement_invalid)
{
- CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected token \"(\"");
+ CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
BOOST_CHECK("{ if calldatasize() {}");
CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
- CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected token LBrace");
+ CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'");
}
BOOST_AUTO_TEST_CASE(switch_statement)
@@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
{
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected.");
- CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected token \"(\"");
+ CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
}
@@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(switch_invalid_case)
BOOST_AUTO_TEST_CASE(switch_invalid_body)
{
- CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected token LBrace got 'Identifier'");
+ CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected '{' but got identifier");
}
BOOST_AUTO_TEST_CASE(for_statement)
@@ -353,10 +353,10 @@ BOOST_AUTO_TEST_CASE(for_statement)
BOOST_AUTO_TEST_CASE(for_invalid_expression)
{
CHECK_PARSE_ERROR("{ for {} {} {} {} }", ParserError, "Literal, identifier or instruction expected.");
- CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected token LBrace got 'Number'");
- CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected token LBrace got 'Number'");
- CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected token LBrace got 'Number'");
- CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected token \"(\"");
+ CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected '{' but got 'Number'");
+ CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'");
+ CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'");
+ CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context");
}
@@ -437,13 +437,13 @@ BOOST_AUTO_TEST_CASE(invalid_tuple_assignment)
BOOST_AUTO_TEST_CASE(instruction_too_few_arguments)
{
- CHECK_PARSE_ERROR("{ mul() }", ParserError, "Expected expression (\"mul\" expects 2 arguments)");
- CHECK_PARSE_ERROR("{ mul(1) }", ParserError, "Expected comma (\"mul\" expects 2 arguments)");
+ CHECK_PARSE_ERROR("{ mul() }", ParserError, "Expected expression (instruction \"mul\" expects 2 arguments)");
+ CHECK_PARSE_ERROR("{ mul(1) }", ParserError, "Expected ',' (instruction \"mul\" expects 2 arguments)");
}
BOOST_AUTO_TEST_CASE(instruction_too_many_arguments)
{
- CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (\"mul\" expects 2 arguments)");
+ CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (instruction \"mul\" expects 2 arguments)");
}
BOOST_AUTO_TEST_CASE(recursion_depth)
diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp
index beb933a4..71fdb906 100644
--- a/test/libsolidity/SMTChecker.cpp
+++ b/test/libsolidity/SMTChecker.cpp
@@ -467,6 +467,82 @@ BOOST_AUTO_TEST_CASE(bool_int_mixed)
CHECK_SUCCESS_NO_WARNINGS(text);
}
+BOOST_AUTO_TEST_CASE(storage_value_vars)
+{
+ string text = R"(
+ contract C
+ {
+ address a;
+ bool b;
+ uint c;
+ function f(uint x) public {
+ if (x == 0)
+ {
+ a = 100;
+ b = true;
+ }
+ else
+ {
+ a = 200;
+ b = false;
+ }
+ assert(a > 0 && b);
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Assertion violation happens here");
+ text = R"(
+ contract C
+ {
+ address a;
+ bool b;
+ uint c;
+ function f() public view {
+ assert(c > 0);
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Assertion violation happens here");
+ text = R"(
+ contract C
+ {
+ function f(uint x) public {
+ if (x == 0)
+ {
+ a = 100;
+ b = true;
+ }
+ else
+ {
+ a = 200;
+ b = false;
+ }
+ assert(b == (a < 200));
+ }
+
+ function g() public view {
+ require(a < 100);
+ assert(c >= 0);
+ }
+ address a;
+ bool b;
+ uint c;
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C
+ {
+ function f() public view {
+ assert(c > 0);
+ }
+ uint c;
+ }
+ )";
+ CHECK_WARNING(text, "Assertion violation happens here");
+
+}
+
BOOST_AUTO_TEST_CASE(while_loop_simple)
{
// Check that variables are cleared
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 789b89d1..1efcfde0 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -2105,7 +2105,7 @@ BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types)
char const* sourceCode = R"(
contract test {
uint120[3] x;
- function f() view returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) {
+ function f() returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) {
uint120[] memory y = new uint120[](3);
x[0] = y[0] = uint120(-2);
x[1] = y[1] = uint120(-3);
@@ -3959,6 +3959,51 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes)
ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80));
}
+BOOST_AUTO_TEST_CASE(call_forward_bytes_length)
+{
+ char const* sourceCode = R"(
+ contract receiver {
+ uint public calledLength;
+ function() { calledLength = msg.data.length; }
+ }
+ contract sender {
+ receiver rec;
+ constructor() { rec = new receiver(); }
+ function viaCalldata() returns (uint) {
+ require(rec.call(msg.data));
+ return rec.calledLength();
+ }
+ function viaMemory() returns (uint) {
+ bytes memory x = msg.data;
+ require(rec.call(x));
+ return rec.calledLength();
+ }
+ bytes s;
+ function viaStorage() returns (uint) {
+ s = msg.data;
+ require(rec.call(s));
+ return rec.calledLength();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "sender");
+
+ // No additional data, just function selector
+ ABI_CHECK(callContractFunction("viaCalldata()"), encodeArgs(4));
+ ABI_CHECK(callContractFunction("viaMemory()"), encodeArgs(0x20));
+ // Should be this with 0.5.0: encodeArgs(4));
+ ABI_CHECK(callContractFunction("viaStorage()"), encodeArgs(0x20));
+ // Should be this with 0.5.0: encodeArgs(4));
+
+ // Some additional unpadded data
+ bytes unpadded = asBytes(string("abc"));
+ ABI_CHECK(callContractFunctionNoEncoding("viaCalldata()", unpadded), encodeArgs(7));
+ ABI_CHECK(callContractFunctionNoEncoding("viaMemory()", unpadded), encodeArgs(0x20));
+ // Should be this with 0.5.0: encodeArgs(7));
+ ABI_CHECK(callContractFunctionNoEncoding("viaStorage()", unpadded), encodeArgs(0x20));
+ // Should be this with 0.5.0: encodeArgs(7));
+}
+
BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
{
char const* sourceCode = R"(
@@ -7610,6 +7655,33 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration)
ABI_CHECK(callContractFunction("f()", encodeArgs()), encodeArgs(true));
}
+BOOST_AUTO_TEST_CASE(typed_multi_variable_declaration)
+{
+ char const* sourceCode = R"(
+ contract C {
+ struct S { uint x; }
+ S s;
+ function g() internal returns (uint, S storage, uint) {
+ s.x = 7;
+ return (1, s, 2);
+ }
+ function f() returns (bool) {
+ (uint x1, S storage y1, uint z1) = g();
+ if (x1 != 1 || y1.x != 7 || z1 != 2) return false;
+ (, S storage y2,) = g();
+ if (y2.x != 7) return false;
+ (uint x2,,) = g();
+ if (x2 != 1) return false;
+ (,,uint z2) = g();
+ if (z2 != 2) return false;
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()", encodeArgs()), encodeArgs(true));
+}
+
BOOST_AUTO_TEST_CASE(tuples)
{
char const* sourceCode = R"(
@@ -10122,6 +10194,23 @@ BOOST_AUTO_TEST_CASE(shift_right_assignment)
ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0)));
}
+BOOST_AUTO_TEST_CASE(shift_right_assignment_signed)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int a, int b) returns (int) {
+ a >>= b;
+ return a;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(17)), encodeArgs(u256(0)));
+}
+
BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue)
{
char const* sourceCode = R"(
@@ -10133,9 +10222,141 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue)
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-266)));
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-16)));
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-266)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-16)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int8)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int8 a, int8 b) returns (int) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(0)), encodeArgs(u256(-66)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(1)), encodeArgs(u256(-33)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(4)), encodeArgs(u256(-4)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(8)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(17)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(0)), encodeArgs(u256(-67)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(1)), encodeArgs(u256(-33)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(4)), encodeArgs(u256(-4)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(8)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(17)), encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int8)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int8 a, int8 b) returns (int8) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(0)), encodeArgs(u256(-103)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), encodeArgs(u256(-51)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), encodeArgs(u256(-25)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(4)), encodeArgs(u256(-6)));
+ ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(8)), encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int16)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int16 a, int16 b) returns (int16) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(0)), encodeArgs(u256(-103)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), encodeArgs(u256(-51)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), encodeArgs(u256(-25)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(4)), encodeArgs(u256(-6)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(8)), encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int32)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int32 a, int32 b) returns (int32) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(0)), encodeArgs(u256(-103)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), encodeArgs(u256(-51)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), encodeArgs(u256(-25)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(4)), encodeArgs(u256(-6)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(8)), encodeArgs(u256(0)));
+}
+
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int16)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int16 a, int16 b) returns (int) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(0)), encodeArgs(u256(-4266)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(4)), encodeArgs(u256(-266)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(8)), encodeArgs(u256(-16)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(17)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(0)), encodeArgs(u256(-4267)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(4)), encodeArgs(u256(-266)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(8)), encodeArgs(u256(-16)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(17)), encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int32)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f(int32 a, int32 b) returns (int) {
+ return a >> b;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(0)), encodeArgs(u256(-4266)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(4)), encodeArgs(u256(-266)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(8)), encodeArgs(u256(-16)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(17)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(0)), encodeArgs(u256(-4267)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(4)), encodeArgs(u256(-266)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(8)), encodeArgs(u256(-16)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(17)), encodeArgs(u256(0)));
}
BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment)
@@ -10150,9 +10371,17 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment)
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-266)));
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-16)));
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(0)));
ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2133)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-266)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-16)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(0)));
+ ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(0)));
}
BOOST_AUTO_TEST_CASE(shift_negative_rvalue)
@@ -10503,12 +10732,24 @@ BOOST_AUTO_TEST_CASE(revert_with_cause)
{
char const* sourceCode = R"(
contract D {
+ string constant msg1 = "test1234567890123456789012345678901234567890";
+ string msg2 = "test1234567890123456789012345678901234567890";
function f() public {
revert("test123");
}
function g() public {
revert("test1234567890123456789012345678901234567890");
}
+ function h() public {
+ revert(msg1);
+ }
+ function i() public {
+ revert(msg2);
+ }
+ function j() public {
+ string memory msg3 = "test1234567890123456789012345678901234567890";
+ revert(msg3);
+ }
}
contract C {
D d = new D();
@@ -10529,6 +10770,15 @@ BOOST_AUTO_TEST_CASE(revert_with_cause)
function g() public returns (bool, bytes) {
return forward(address(d), msg.data);
}
+ function h() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function i() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function j() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
}
)";
compileAndRun(sourceCode, 0, "C");
@@ -10536,6 +10786,9 @@ BOOST_AUTO_TEST_CASE(revert_with_cause)
bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0};
ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "test123") + bytes(28, 0) : bytes());
ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0): bytes());
+ ABI_CHECK(callContractFunction("h()"), haveReturndata ? encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0): bytes());
+ ABI_CHECK(callContractFunction("i()"), haveReturndata ? encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0): bytes());
+ ABI_CHECK(callContractFunction("j()"), haveReturndata ? encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0): bytes());
}
BOOST_AUTO_TEST_CASE(require_with_message)
@@ -10544,6 +10797,7 @@ BOOST_AUTO_TEST_CASE(require_with_message)
contract D {
bool flag = false;
string storageError = "abc";
+ string constant constantError = "abc";
function f(uint x) public {
require(x > 7, "failed");
}
@@ -10561,6 +10815,13 @@ BOOST_AUTO_TEST_CASE(require_with_message)
function h() public {
require(false, storageError);
}
+ function i() public {
+ require(false, constantError);
+ }
+ function j() public {
+ string memory errMsg = "msg";
+ require(false, errMsg);
+ }
}
contract C {
D d = new D();
@@ -10584,6 +10845,12 @@ BOOST_AUTO_TEST_CASE(require_with_message)
function h() public returns (bool, bytes) {
return forward(address(d), msg.data);
}
+ function i() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function j() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
}
)";
compileAndRun(sourceCode, 0, "C");
@@ -10594,6 +10861,8 @@ BOOST_AUTO_TEST_CASE(require_with_message)
ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(1, 0x40, 0) : bytes());
ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 18, "only on second run") + bytes(28, 0) : bytes());
ABI_CHECK(callContractFunction("h()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0): bytes());
+ ABI_CHECK(callContractFunction("i()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0): bytes());
+ ABI_CHECK(callContractFunction("j()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "msg") + bytes(28, 0): bytes());
}
BOOST_AUTO_TEST_CASE(bubble_up_error_messages)
@@ -10720,9 +10989,9 @@ BOOST_AUTO_TEST_CASE(negative_stack_height)
bool Aboolc;
bool exists;
}
- function nredit(uint startindex) public constant returns(uint[500] CIDs, uint[500] dates, uint[500] RIDs, bool[500] Cboolas, uint[500] amounts){}
- function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public constant returns(uint[500] AIDs, bool[500] Aboolas, uint[500] dates, bytes32[3][500] Abytesas, bytes32[3][500] bytesbs, bytes32[2][500] bytescs, uint[500] amounts, bool[500] Aboolbs, bool[500] Aboolcs){}
- function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public constant returns(uint[500] BIDs, uint[500] dates, uint[500] RIDs, bool[500] Bboolas, bytes32[3][500] bytesbs,bytes32[2][500] bytescs, uint[500] amounts, bool[500] Bboolbs){}
+ function nredit(uint startindex) public pure returns(uint[500] CIDs, uint[500] dates, uint[500] RIDs, bool[500] Cboolas, uint[500] amounts){}
+ function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] AIDs, bool[500] Aboolas, uint[500] dates, bytes32[3][500] Abytesas, bytes32[3][500] bytesbs, bytes32[2][500] bytescs, uint[500] amounts, bool[500] Aboolbs, bool[500] Aboolcs){}
+ function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] BIDs, uint[500] dates, uint[500] RIDs, bool[500] Bboolas, bytes32[3][500] bytesbs,bytes32[2][500] bytescs, uint[500] amounts, bool[500] Bboolbs){}
}
)";
compileAndRun(sourceCode, 0, "C");
@@ -10908,13 +11177,13 @@ BOOST_AUTO_TEST_CASE(bare_call_invalid_address)
char const* sourceCode = R"(
contract C {
/// Calling into non-existant account is successful (creates the account)
- function f() external view returns (bool) {
+ function f() external returns (bool) {
return address(0x4242).call();
}
- function g() external view returns (bool) {
+ function g() external returns (bool) {
return address(0x4242).callcode();
}
- function h() external view returns (bool) {
+ function h() external returns (bool) {
return address(0x4242).delegatecall();
}
}
@@ -10936,13 +11205,13 @@ BOOST_AUTO_TEST_CASE(delegatecall_return_value)
function get() external view returns (uint) {
return value;
}
- function get_delegated() external view returns (bool) {
+ function get_delegated() external returns (bool) {
return this.delegatecall(bytes4(sha3("get()")));
}
function assert0() external view {
assert(value == 0);
}
- function assert0_delegated() external view returns (bool) {
+ function assert0_delegated() external returns (bool) {
return this.delegatecall(bytes4(sha3("assert0()")));
}
}
@@ -11403,7 +11672,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_v2)
require(y[0] == "e");
}
S s;
- function f4() public view returns (bytes r) {
+ function f4() public returns (bytes r) {
string memory x = "abc";
s.a = 7;
s.b.push(2);
@@ -11881,6 +12150,26 @@ BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople)
BOOST_CHECK(callContractFunction("shr_3()") == encodeArgs(u256(1)));
}
+BOOST_AUTO_TEST_CASE(senders_balance)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() public view returns (uint) {
+ return msg.sender.balance;
+ }
+ }
+ contract D {
+ C c = new C();
+ constructor() payable { }
+ function f() public view returns (uint) {
+ return c.f();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 27, "D");
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 97cf0410..5af67659 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -831,24 +831,6 @@ BOOST_AUTO_TEST_CASE(illegal_override_visibility)
CHECK_ERROR(text, TypeError, "Overriding function visibility differs");
}
-BOOST_AUTO_TEST_CASE(illegal_override_remove_constness)
-{
- char const* text = R"(
- contract B { function f() constant {} }
- contract C is B { function f() public {} }
- )";
- CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"view\" to \"nonpayable\".");
-}
-
-BOOST_AUTO_TEST_CASE(illegal_override_add_constness)
-{
- char const* text = R"(
- contract B { function f() public {} }
- contract C is B { function f() constant {} }
- )";
- CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"nonpayable\" to \"view\".");
-}
-
BOOST_AUTO_TEST_CASE(complex_inheritance)
{
char const* text = R"(
@@ -993,19 +975,6 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist");
}
-BOOST_AUTO_TEST_CASE(missing_state_variable)
-{
- char const* text = R"(
- contract Scope {
- function getStateVar() constant public returns (uint stateVar) {
- stateVar = Scope.stateVar; // should fail.
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Member \"stateVar\" not found or not visible after argument-dependent lookup in type(contract Scope)");
-}
-
-
BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor)
{
// test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126
@@ -1119,17 +1088,6 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters)
CHECK_ERROR(text, TypeError, "Fallback function cannot return values.");
}
-BOOST_AUTO_TEST_CASE(fallback_function_with_constant_modifier)
-{
- char const* text = R"(
- contract C {
- uint x;
- function() constant { x = 2; }
- }
- )";
- CHECK_ERROR(text, TypeError, "Fallback function must be payable or non-payable");
-}
-
BOOST_AUTO_TEST_CASE(fallback_function_twice)
{
char const* text = R"(
@@ -2278,25 +2236,6 @@ BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx)
CHECK_SUCCESS_NO_WARNINGS(text);
}
-BOOST_AUTO_TEST_CASE(combining_hex_and_denomination)
-{
- char const* text = R"(
- contract Foo {
- uint constant x = 0x01 wei;
- }
- )";
- CHECK_WARNING(text, "Hexadecimal numbers with unit denominations are deprecated.");
-
- char const* textV050 = R"(
- pragma experimental "v0.5.0";
-
- contract Foo {
- uint constant x = 0x01 wei;
- }
- )";
- CHECK_ERROR(textV050, TypeError, "Hexadecimal numbers cannot be used with unit denominations.");
-}
-
BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable)
{
char const* text = R"(
@@ -2346,30 +2285,6 @@ BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment)
CHECK_ERROR(text, TypeError, "Index access for string is not possible.");
}
-BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant_0_4_x)
-{
- char const* text = R"(
- contract C {
- function () constant returns (uint) x;
- uint constant y = x();
- }
- )";
- CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant.");
-}
-
-BOOST_AUTO_TEST_CASE(assign_constant_function_value_to_constant)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
-
- contract C {
- function () constant returns (uint) x;
- uint constant y = x();
- }
- )";
- CHECK_ERROR(text, TypeError, "Initial value for constant variable has to be compile-time constant.");
-}
-
BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_conversion)
{
char const* text = R"(
@@ -2913,7 +2828,7 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
contract C {
function f(uint) public returns (string);
function g() public {
- var (x,) = this.f(2);
+ var x = this.f(2);
// we can assign to x but it is not usable.
bytes(x).length;
}
@@ -5823,7 +5738,7 @@ BOOST_AUTO_TEST_CASE(bare_others)
CHECK_WARNING("contract C { function f() pure public { assert; } }", "Statement has no effect.");
// This is different because it does have overloads.
CHECK_ERROR("contract C { function f() pure public { require; } }", TypeError, "No matching declaration found after variable lookup.");
- CHECK_WARNING("contract C { function f() pure public { suicide; } }", "Statement has no effect.");
+ CHECK_WARNING("contract C { function f() pure public { selfdestruct; } }", "Statement has no effect.");
}
BOOST_AUTO_TEST_CASE(pure_statement_in_for_loop)
@@ -5852,80 +5767,6 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- (x, y) = (y, x);
- }
- }
- )";
- CHECK_WARNING(text, "This assignment performs two copies to storage.");
-}
-
-BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_right)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- (x, y, ) = (y, x, 1, 2);
- }
- }
- )";
- CHECK_WARNING(text, "This assignment performs two copies to storage.");
-}
-
-BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_left)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- (,x, y) = (1, 2, y, x);
- }
- }
- )";
- CHECK_WARNING(text, "This assignment performs two copies to storage.");
-}
-
-BOOST_AUTO_TEST_CASE(nowarn_swap_memory)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- function f() pure public {
- S memory x;
- S memory y;
- (x, y) = (y, x);
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(nowarn_swap_storage_pointers)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; uint b; }
- S x; S y;
- function f() public {
- S storage x_local = x;
- S storage y_local = y;
- S storage z_local = x;
- (x, y_local, x_local, z_local) = (y, x_local, y_local, y);
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
BOOST_AUTO_TEST_CASE(warn_unused_local)
{
char const* text = R"(
@@ -6958,31 +6799,6 @@ BOOST_AUTO_TEST_CASE(invalid_literal_in_tuple)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(warn_about_sha3)
-{
- char const* text = R"(
- contract test {
- function f() pure public {
- bytes32 x = sha3(uint8(1));
- x;
- }
- }
- )";
- CHECK_WARNING(text, "\"sha3\" has been deprecated in favour of \"keccak256\"");
-}
-
-BOOST_AUTO_TEST_CASE(warn_about_suicide)
-{
- char const* text = R"(
- contract test {
- function f() public {
- suicide(1);
- }
- }
- )";
- CHECK_WARNING(text, "\"suicide\" has been deprecated in favour of \"selfdestruct\"");
-}
-
BOOST_AUTO_TEST_CASE(address_overload_resolution)
{
char const* text = R"(
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 100b3662..0c801cf6 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -112,93 +112,6 @@ while(0)
BOOST_AUTO_TEST_SUITE(SolidityParser)
-BOOST_AUTO_TEST_CASE(empty_function)
-{
- char const* text = R"(
- contract test {
- uint256 stateVar;
- function functionName(bytes20 arg1, address addr) constant
- returns (int id)
- { }
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(no_function_params)
-{
- char const* text = R"(
- contract test {
- uint256 stateVar;
- function functionName() {}
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(single_function_param)
-{
- char const* text = R"(
- contract test {
- uint256 stateVar;
- function functionName(bytes32 input) returns (bytes32 out) {}
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(single_function_param_trailing_comma)
-{
- char const* text = R"(
- contract test {
- function(uint a,) {}
- }
- )";
- CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list.");
-}
-
-BOOST_AUTO_TEST_CASE(single_return_param_trailing_comma)
-{
- char const* text = R"(
- contract test {
- function() returns (uint a,) {}
- }
- )";
- CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list.");
-}
-
-BOOST_AUTO_TEST_CASE(single_modifier_arg_trailing_comma)
-{
- char const* text = R"(
- contract test {
- modifier modTest(uint a,) { _; }
- function(uint a) {}
- }
- )";
- CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list.");
-}
-
-BOOST_AUTO_TEST_CASE(single_event_arg_trailing_comma)
-{
- char const* text = R"(
- contract test {
- event Test(uint a,);
- function(uint a) {}
- }
- )";
- CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list.");
-}
-
-BOOST_AUTO_TEST_CASE(multiple_function_param_trailing_comma)
-{
- char const* text = R"(
- contract test {
- function(uint a, uint b,) {}
- }
- )";
- CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list.");
-}
-
BOOST_AUTO_TEST_CASE(multiple_return_param_trailing_comma)
{
char const* text = R"(
@@ -231,49 +144,6 @@ BOOST_AUTO_TEST_CASE(multiple_event_arg_trailing_comma)
CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list.");
}
-BOOST_AUTO_TEST_CASE(function_no_body)
-{
- char const* text = R"(
- contract test {
- function functionName(bytes32 input) returns (bytes32 out);
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(missing_parameter_name_in_named_args)
-{
- char const* text = R"(
- contract test {
- function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
- function b() returns (uint r) { r = a({: 1, : 2, : 3}); }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
-BOOST_AUTO_TEST_CASE(missing_argument_in_named_args)
-{
- char const* text = R"(
- contract test {
- function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
- function b() returns (uint r) { r = a({a: , b: , c: }); }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected primary expression");
-}
-
-BOOST_AUTO_TEST_CASE(trailing_comma_in_named_args)
-{
- char const* text = R"(
- contract test {
- function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
- function b() returns (uint r) { r = a({a: 1, b: 2, c: 3, }); }
- }
- )";
- CHECK_PARSE_ERROR(text, "Unexpected trailing comma");
-}
-
BOOST_AUTO_TEST_CASE(two_exact_functions)
{
char const* text = R"(
@@ -288,17 +158,6 @@ BOOST_AUTO_TEST_CASE(two_exact_functions)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(overloaded_functions)
-{
- char const* text = R"(
- contract test {
- function fun(uint a) returns(uint r) { return a; }
- function fun(uint a, uint b) returns(uint r) { return a + b; }
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
BOOST_AUTO_TEST_CASE(function_natspec_documentation)
{
char const* text = R"(
@@ -557,18 +416,6 @@ BOOST_AUTO_TEST_CASE(variable_definition_with_initialization)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(variable_definition_in_mapping)
-{
- char const* text = R"(
- contract test {
- function fun() {
- mapping(var=>bytes32) d;
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected elementary type name for mapping key type");
-}
-
BOOST_AUTO_TEST_CASE(operator_expression)
{
char const* text = R"(
@@ -849,16 +696,6 @@ BOOST_AUTO_TEST_CASE(modifier)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(modifier_without_semicolon)
-{
- char const* text = R"(
- contract c {
- modifier mod { if (msg.sender == 0) _ }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected token Semicolon got");
-}
-
BOOST_AUTO_TEST_CASE(modifier_arguments)
{
char const* text = R"(
@@ -918,16 +755,6 @@ BOOST_AUTO_TEST_CASE(event_arguments_indexed)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(event_with_no_argument_list_fails)
-{
- char const* text = R"(
- contract c {
- event e;
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'");
-}
-
BOOST_AUTO_TEST_CASE(visibility_specifiers)
{
char const* text = R"(
@@ -958,40 +785,6 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers)
CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\".");
}
-BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers)
-{
- char const* text = R"(
- contract c {
- function f() payable payable {}
- })";
- CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
- text = R"(
- contract c {
- function f() constant constant {}
- })";
- CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
- text = R"(
- contract c {
- function f() constant view {}
- })";
- CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
- text = R"(
- contract c {
- function f() payable constant {}
- })";
- CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
- text = R"(
- contract c {
- function f() pure payable {}
- })";
- CHECK_PARSE_ERROR(text, "State mutability already specified as \"pure\".");
- text = R"(
- contract c {
- function f() pure constant {}
- })";
- CHECK_PARSE_ERROR(text, "State mutability already specified as \"pure\".");
-}
-
BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations)
{
char const* text = R"(
@@ -1038,24 +831,6 @@ BOOST_AUTO_TEST_CASE(enum_valid_declaration)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(empty_enum_declaration)
-{
- char const* text = R"(
- contract c {
- enum foo { }
- })";
- CHECK_PARSE_ERROR(text, "enum with no members is not allowed");
-}
-
-BOOST_AUTO_TEST_CASE(malformed_enum_declaration)
-{
- char const* text = R"(
- contract c {
- enum foo { WARNING,}
- })";
- CHECK_PARSE_ERROR(text, "Expected Identifier after");
-}
-
BOOST_AUTO_TEST_CASE(external_function)
{
char const* text = R"(
@@ -1065,15 +840,6 @@ BOOST_AUTO_TEST_CASE(external_function)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(external_variable)
-{
- char const* text = R"(
- contract c {
- uint external x;
- })";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
BOOST_AUTO_TEST_CASE(arrays_in_storage)
{
char const* text = R"(
@@ -1113,15 +879,6 @@ BOOST_AUTO_TEST_CASE(multi_arrays)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(constant_is_keyword)
-{
- char const* text = R"(
- contract Foo {
- uint constant = 4;
- })";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
BOOST_AUTO_TEST_CASE(keyword_is_reserved)
{
auto keywords = {
@@ -1148,19 +905,10 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved)
for (const auto& keyword: keywords)
{
auto text = std::string("contract ") + keyword + " {}";
- CHECK_PARSE_ERROR(text.c_str(), "Expected identifier");
+ CHECK_PARSE_ERROR(text.c_str(), "Expected identifier but got reserved keyword");
}
}
-BOOST_AUTO_TEST_CASE(var_array)
-{
- char const* text = R"(
- contract Foo {
- function f() { var[] a; }
- })";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
BOOST_AUTO_TEST_CASE(location_specifiers_for_params)
{
char const* text = R"(
@@ -1184,47 +932,6 @@ BOOST_AUTO_TEST_CASE(location_specifiers_for_locals)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(location_specifiers_for_state)
-{
- char const* text = R"(
- contract Foo {
- uint[] memory x;
- })";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
-BOOST_AUTO_TEST_CASE(location_specifiers_with_var)
-{
- char const* text = R"(
- contract Foo {
- function f() { var memory x; }
- })";
- CHECK_PARSE_ERROR(text, "Location specifier needs explicit type name");
-}
-
-BOOST_AUTO_TEST_CASE(empty_comment)
-{
- char const* text = R"(
- //
- contract test
- {}
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(comment_end_with_double_star)
-{
- char const* text = R"(
- contract C1 {
- /**
- **/
- }
- contract C2 {}
- )";
- BOOST_CHECK(successParse(text));
-}
-
-
BOOST_AUTO_TEST_CASE(library_simple)
{
char const* text = R"(
@@ -1235,68 +942,6 @@ BOOST_AUTO_TEST_CASE(library_simple)
BOOST_CHECK(successParse(text));
}
-
-BOOST_AUTO_TEST_CASE(local_const_variable)
-{
- char const* text = R"(
- contract Foo {
- function localConst() returns (uint ret)
- {
- uint constant local = 4;
- return local;
- }
- })";
- CHECK_PARSE_ERROR(text, "Expected token Semicolon");
-}
-
-BOOST_AUTO_TEST_CASE(multi_variable_declaration)
-{
- char const* text = R"(
- contract C {
- function f() {
- var (a,b,c) = g();
- var (d) = 2;
- var (,e) = 3;
- var (f,) = 4;
- var (x,,) = g();
- var (,y,) = g();
- var () = g();
- var (,,) = g();
- }
- function g() returns (uint, uint, uint) {}
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(tuples)
-{
- char const* text = R"(
- contract C {
- function f() {
- uint a = (1);
- var (b,) = (1,);
- var (c,d) = (1, 2 + a);
- var (e,) = (1, 2, b);
- (a) = 3;
- }
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(tuples_without_commas)
-{
- char const* text = R"(
- contract C {
- function f() {
- var a = (2 2);
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected token Comma");
-}
-
BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity)
{
char const* text = R"(
@@ -1365,34 +1010,6 @@ BOOST_AUTO_TEST_CASE(inline_array_declaration)
BOOST_CHECK(successParse(text));
}
-
-BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_lvalue)
-{
- char const* text = R"(
- contract c {
- uint[] a;
- function f() returns (uint) {
- a = [,2,3];
- return (a[0]);
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected expression");
-}
-
-BOOST_AUTO_TEST_CASE(inline_array_empty_cells_check_without_lvalue)
-{
- char const* text = R"(
- contract c {
- uint[] a;
- function f() returns (uint, uint) {
- return ([3, ,4][0]);
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected expression");
-}
-
BOOST_AUTO_TEST_CASE(conditional_true_false_literal)
{
char const* text = R"(
@@ -1520,38 +1137,6 @@ BOOST_AUTO_TEST_CASE(declaring_fixed_literal_variables)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(no_double_radix_in_fixed_literal)
-{
- char const* text = R"(
- contract A {
- fixed40x40 pi = 3.14.15;
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected token Semicolon");
-}
-
-BOOST_AUTO_TEST_CASE(invalid_fixed_conversion_leading_zeroes_check)
-{
- char const* text = R"(
- contract test {
- function f() {
- fixed a = 1.0x2;
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected primary expression");
-}
-
-BOOST_AUTO_TEST_CASE(payable_accessor)
-{
- char const* text = R"(
- contract test {
- uint payable x;
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
BOOST_AUTO_TEST_CASE(function_type_in_expression)
{
char const* text = R"(
@@ -1575,16 +1160,6 @@ BOOST_AUTO_TEST_CASE(function_type_as_storage_variable)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_modifiers)
-{
- char const* text = R"(
- contract test {
- function (uint, uint) modifier1() returns (uint) f1;
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected token LBrace");
-}
-
BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_assignment)
{
char const* text = R"(
@@ -1649,66 +1224,6 @@ BOOST_AUTO_TEST_CASE(mapping_and_array_of_functions)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(function_type_state_variable)
-{
- char const* text = R"(
- contract test {
- function() x;
- function() y = x;
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(scientific_notation)
-{
- char const* text = R"(
- contract test {
- uint256 a = 2e10;
- uint256 b = 2E10;
- uint256 c = 200e-2;
- uint256 d = 2E10 wei;
- uint256 e = 2.5e10;
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(interface)
-{
- char const* text = R"(
- interface Interface {
- function f();
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(newInvalidTypeName)
-{
- char const* text = R"(
- contract C {
- function f() {
- new var;
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected explicit type name");
-}
-
-BOOST_AUTO_TEST_CASE(emitWithoutEvent)
-{
- char const* text = R"(
- contract C {
- event A();
- function f() {
- emit A;
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'");
-}
-
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 74bf01b2..63c03881 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -326,9 +326,9 @@ BOOST_AUTO_TEST_CASE(compilation_error)
{
BOOST_CHECK_EQUAL(
dev::jsonCompactPrint(error),
- "{\"component\":\"general\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected identifier, got 'RBrace'\\n"
- "contract A { function }\\n ^\\n\",\"message\":\"Expected identifier, got 'RBrace'\","
- "\"severity\":\"error\",\"sourceLocation\":{\"end\":22,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}"
+ "{\"component\":\"general\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected identifier but got '}'\\n"
+ "contract A { function }\\n ^\\n\",\"message\":\"Expected identifier but got '}'\","
+ "\"severity\":\"error\",\"sourceLocation\":{\"end\":23,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}"
);
}
}
diff --git a/test/libsolidity/syntaxTests/constants/abi_encoding_constant.sol b/test/libsolidity/syntaxTests/constants/abi_encoding_constant.sol
new file mode 100644
index 00000000..dd6968a0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/abi_encoding_constant.sol
@@ -0,0 +1,7 @@
+contract C {
+ bytes32 constant a = keccak256(abi.encode(1, 2));
+ bytes32 constant b = keccak256(abi.encodePacked(uint(1), a));
+ bytes32 constant c = keccak256(abi.encodeWithSelector(0x12345678, b, 2));
+ bytes32 constant d = keccak256(abi.encodeWithSignature("f()", 1, 2));
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol b/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol
new file mode 100644
index 00000000..88e94e29
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol
@@ -0,0 +1,6 @@
+contract C {
+ function () pure returns (uint) x;
+ uint constant y = x();
+}
+// ----
+// Warning: (74-77): Initial value for constant variable has to be compile-time constant. This will fail to compile with the next breaking version change.
diff --git a/test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol b/test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol
new file mode 100644
index 00000000..2c92899d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol
@@ -0,0 +1,8 @@
+pragma experimental "v0.5.0";
+
+contract C {
+ function () pure returns (uint) x;
+ uint constant y = x();
+}
+// ----
+// TypeError: (105-108): Initial value for constant variable has to be compile-time constant.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
index 00f9bb0f..08d20c3a 100644
--- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
@@ -5,6 +5,8 @@ contract C {
uint constant d = 2 + a;
}
// ----
+// Warning: (98-110): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (98-110): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory.
// TypeError: (17-40): The value of the constant a has a cyclic dependency via c.
// TypeError: (71-111): The value of the constant c has a cyclic dependency via d.
// TypeError: (117-140): The value of the constant d has a cyclic dependency via a.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
index f01cb98e..df5cd969 100644
--- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
@@ -3,4 +3,7 @@ contract C {
uint constant b = 7;
uint constant c = 4 + uint(keccak256(d));
uint constant d = 2 + b;
-} \ No newline at end of file
+}
+// ----
+// Warning: (98-110): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (98-110): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol
new file mode 100644
index 00000000..65902cc8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol
@@ -0,0 +1,26 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal returns (S storage c) {
+ assembly {
+ sstore(c_slot, sload(s_slot))
+ }
+ }
+ function g(bool flag) internal returns (S storage c) {
+ // control flow in assembly will not be analyzed for now,
+ // so this will not issue a warning
+ assembly {
+ if flag {
+ sstore(c_slot, sload(s_slot))
+ }
+ }
+ }
+ function h() internal returns (S storage c) {
+ // any reference from assembly will be sufficient for now,
+ // so this will not issue a warning
+ assembly {
+ sstore(s_slot, sload(c_slot))
+ }
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol
new file mode 100644
index 00000000..09c13847
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol
@@ -0,0 +1,10 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal pure returns (S storage) {
+ assembly {
+ }
+ }
+}
+// ----
+// Warning: (87-88): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol
new file mode 100644
index 00000000..9a42192d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol
@@ -0,0 +1,19 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S c) {
+ c = s;
+ }
+ function g() internal view returns (S) {
+ return s;
+ }
+ function h() internal pure returns (S) {
+ }
+ function i(bool flag) internal view returns (S c) {
+ if (flag) c = s;
+ }
+ function j(bool flag) internal view returns (S) {
+ if (flag) return s;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol
new file mode 100644
index 00000000..6520672c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol
@@ -0,0 +1,36 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ do {} while((c = s).f);
+ }
+ function g() internal view returns (S storage c) {
+ do { c = s; } while(false);
+ }
+ function h() internal view returns (S storage c) {
+ c = s;
+ do {} while(false);
+ }
+ function i() internal view returns (S storage c) {
+ do {} while(false);
+ c = s;
+ }
+ function j() internal view returns (S storage c) {
+ do {
+ c = s;
+ break;
+ } while(false);
+ }
+ function k() internal view returns (S storage c) {
+ do {
+ if (s.f) {
+ continue;
+ break;
+ }
+ else {
+ c = s;
+ }
+ } while(false);
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol
new file mode 100644
index 00000000..f1a92e9c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol
@@ -0,0 +1,35 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ do {
+ break;
+ c = s;
+ } while(false);
+ }
+ function g() internal view returns (S storage c) {
+ do {
+ if (s.f) {
+ continue;
+ c = s;
+ }
+ else {
+ }
+ } while(false);
+ }
+ function h() internal view returns (S storage c) {
+ do {
+ if (s.f) {
+ break;
+ continue;
+ }
+ else {
+ c = s;
+ }
+ } while(false);
+ }
+}
+// ----
+// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (223-234): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (440-451): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol
new file mode 100644
index 00000000..3a0a30ea
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol
@@ -0,0 +1,6 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c, S storage d) { c = s; d = s; return; }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol
new file mode 100644
index 00000000..0a5b2fbf
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol
@@ -0,0 +1,15 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal pure returns (S storage) { return; }
+ function g() internal view returns (S storage c, S storage) { c = s; return; }
+ function h() internal view returns (S storage, S storage d) { d = s; return; }
+ function i() internal pure returns (S storage, S storage) { return; }
+ function j() internal view returns (S storage, S storage) { return (s,s); }
+}
+// ----
+// Warning: (87-88): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (163-164): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (233-234): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (316-317): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (327-328): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_fine.sol
new file mode 100644
index 00000000..aa82cb9a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_fine.sol
@@ -0,0 +1,13 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ for(c = s;;) {
+ }
+ }
+ function g() internal view returns (S storage c) {
+ for(; (c = s).f;) {
+ }
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol
new file mode 100644
index 00000000..ba9a2440
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol
@@ -0,0 +1,16 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ for(;; c = s) {
+ }
+ }
+ function g() internal view returns (S storage c) {
+ for(;;) {
+ c = s;
+ }
+ }
+}
+// ----
+// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (182-193): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_fine.sol
new file mode 100644
index 00000000..b809e95d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_fine.sol
@@ -0,0 +1,29 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f(bool flag) internal view returns (S storage c) {
+ if (flag) c = s;
+ else c = s;
+ }
+ function g(bool flag) internal view returns (S storage c) {
+ if (flag) c = s;
+ else { c = s; }
+ }
+ function h(bool flag) internal view returns (S storage c) {
+ if (flag) c = s;
+ else
+ {
+ if (!flag) c = s;
+ else c = s;
+ }
+ }
+ function i() internal view returns (S storage c) {
+ if ((c = s).f) {
+ }
+ }
+ function j() internal view returns (S storage c) {
+ if ((c = s).f && !(c = s).f) {
+ }
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol
new file mode 100644
index 00000000..c257c252
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol
@@ -0,0 +1,18 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f(bool flag) internal view returns (S storage c) {
+ if (flag) c = s;
+ }
+ function g(bool flag) internal returns (S storage c) {
+ if (flag) c = s;
+ else
+ {
+ if (!flag) c = s;
+ else s.f = true;
+ }
+ }
+}
+// ----
+// Warning: (96-107): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (186-197): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_fine.sol
new file mode 100644
index 00000000..ee37f6d6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_fine.sol
@@ -0,0 +1,20 @@
+contract C {
+ modifier revertIfNoReturn() {
+ _;
+ revert();
+ }
+ modifier ifFlag(bool flag) {
+ if (flag)
+ _;
+ }
+ struct S { uint a; }
+ S s;
+ function f(bool flag) revertIfNoReturn() internal view returns(S storage) {
+ if (flag) return s;
+ }
+ function g(bool flag) revertIfNoReturn() ifFlag(flag) internal view returns(S storage) {
+ return s;
+ }
+
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol
new file mode 100644
index 00000000..50c6dd99
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol
@@ -0,0 +1,22 @@
+contract C {
+ modifier revertIfNoReturn() {
+ _;
+ revert();
+ }
+ modifier ifFlag(bool flag) {
+ if (flag)
+ _;
+ }
+ struct S { uint a; }
+ S s;
+ function f(bool flag) ifFlag(flag) internal view returns(S storage) {
+ return s;
+ }
+
+ function g(bool flag) ifFlag(flag) revertIfNoReturn() internal view returns(S storage) {
+ return s;
+ }
+}
+// ----
+// Warning: (249-250): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (367-368): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_fine.sol
new file mode 100644
index 00000000..022f2d1c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_fine.sol
@@ -0,0 +1,12 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal pure returns (S storage) {
+ revert();
+ }
+ function g(bool flag) internal view returns (S storage c) {
+ if (flag) c = s;
+ else revert();
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_fine.sol
new file mode 100644
index 00000000..699849c0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_fine.sol
@@ -0,0 +1,11 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ (c = s).f && false;
+ }
+ function g() internal view returns (S storage c) {
+ (c = s).f || true;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol
new file mode 100644
index 00000000..9f660f11
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol
@@ -0,0 +1,18 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ false && (c = s).f;
+ }
+ function g() internal view returns (S storage c) {
+ true || (c = s).f;
+ }
+ function h() internal view returns (S storage c) {
+ // expect warning, although this is always fine
+ true && (false || (c = s).f);
+ }
+}
+// ----
+// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (176-187): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (264-275): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke.sol
new file mode 100644
index 00000000..beeadbe4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke.sol
@@ -0,0 +1,10 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal pure {}
+ function g() internal view returns (S storage) { return s; }
+ function h() internal view returns (S storage c) { return s; }
+ function i() internal view returns (S storage c) { c = s; }
+ function j() internal view returns (S storage c) { (c) = s; }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_fine.sol
new file mode 100644
index 00000000..ee3869bd
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_fine.sol
@@ -0,0 +1,14 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f(bool flag) internal view returns (S storage c) {
+ flag ? c = s : c = s;
+ }
+ function g(bool flag) internal view returns (S storage c) {
+ flag ? c = s : (c = s);
+ }
+ function h(bool flag) internal view returns (S storage c) {
+ flag ? (c = s).f : (c = s).f;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol
new file mode 100644
index 00000000..57561fbb
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol
@@ -0,0 +1,13 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f(bool flag) internal view returns (S storage c) {
+ flag ? (c = s).f : false;
+ }
+ function g(bool flag) internal view returns (S storage c) {
+ flag ? false : (c = s).f;
+ }
+}
+// ----
+// Warning: (96-107): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
+// Warning: (200-211): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol
new file mode 100644
index 00000000..4cecc27c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol
@@ -0,0 +1,9 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal pure returns (S storage) {
+ throw;
+ }
+}
+// ----
+// Warning: (108-113): "throw" is deprecated in favour of "revert()", "require()" and "assert()".
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol
new file mode 100644
index 00000000..0b171560
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol
@@ -0,0 +1,12 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage, uint) {
+ return (s,2);
+ }
+ function g() internal view returns (S storage c) {
+ uint a;
+ (c, a) = f();
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_fine.sol
new file mode 100644
index 00000000..71543422
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_fine.sol
@@ -0,0 +1,19 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ while((c = s).f) {
+ }
+ }
+ function g() internal view returns (S storage c) {
+ c = s;
+ while(false) {
+ }
+ }
+ function h() internal view returns (S storage c) {
+ while(false) {
+ }
+ c = s;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol
new file mode 100644
index 00000000..26db892f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol
@@ -0,0 +1,11 @@
+contract C {
+ struct S { bool f; }
+ S s;
+ function f() internal view returns (S storage c) {
+ while(false) {
+ c = s;
+ }
+ }
+}
+// ----
+// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol
new file mode 100644
index 00000000..3571e8a9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant x = 0x01 wei;
+}
+// ----
+// Warning: (32-40): Hexadecimal numbers with unit denominations are deprecated. You can use an expression of the form "0x1234 * 1 day" instead.
diff --git a/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol
new file mode 100644
index 00000000..98865999
--- /dev/null
+++ b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol
@@ -0,0 +1,6 @@
+pragma experimental "v0.5.0";
+contract C {
+ uint constant x = 0x01 wei;
+}
+// ----
+// TypeError: (62-70): Hexadecimal numbers cannot be used with unit denominations. You can use an expression of the form "0x1234 * 1 day" instead.
diff --git a/test/libsolidity/syntaxTests/denominations/denominations.sol b/test/libsolidity/syntaxTests/denominations/denominations.sol
new file mode 100644
index 00000000..6d1aa2f3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/denominations/denominations.sol
@@ -0,0 +1,7 @@
+contract C {
+ uint constant a = 1 wei + 2 szabo + 3 finney + 4 ether;
+ uint constant b = 1 seconds + 2 minutes + 3 hours + 4 days + 5 weeks + 6 years;
+ uint constant c = 2 szabo / 1 seconds + 3 finney * 3 hours;
+}
+// ----
+// Warning: (142-149): Using "years" as a unit denomination is deprecated.
diff --git a/test/libsolidity/syntaxTests/denominations/deprecated_year.sol b/test/libsolidity/syntaxTests/denominations/deprecated_year.sol
new file mode 100644
index 00000000..30e86535
--- /dev/null
+++ b/test/libsolidity/syntaxTests/denominations/deprecated_year.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant a = 3 years;
+}
+// ----
+// Warning: (32-39): Using "years" as a unit denomination is deprecated.
diff --git a/test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol b/test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol
new file mode 100644
index 00000000..4baaeaa3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol
@@ -0,0 +1,6 @@
+pragma experimental "v0.5.0";
+contract C {
+ uint constant a = 3 years;
+}
+// ----
+// TypeError: (62-69): Using "years" as a unit denomination is deprecated.
diff --git a/test/libsolidity/syntaxTests/denominations/fixed_point_division.sol b/test/libsolidity/syntaxTests/denominations/fixed_point_division.sol
new file mode 100644
index 00000000..22331b51
--- /dev/null
+++ b/test/libsolidity/syntaxTests/denominations/fixed_point_division.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint constant a = 4 ether / 3 hours;
+ ufixed constant b = ufixed(4 ether / 3 hours);
+}
+// ----
+// TypeError: (32-49): Type rational_const 10000000000000000 / 27 is not implicitly convertible to expected type uint256. Try converting to type ufixed256x62 or use an explicit conversion.
diff --git a/test/libsolidity/syntaxTests/deprecated_functions.sol b/test/libsolidity/syntaxTests/deprecated_functions.sol
new file mode 100644
index 00000000..9df2b43c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/deprecated_functions.sol
@@ -0,0 +1,12 @@
+contract test {
+ function f() pure public {
+ bytes32 x = sha3();
+ x;
+ }
+ function g() public {
+ suicide(1);
+ }
+}
+// ----
+// Warning: (58-64): "sha3" has been deprecated in favour of "keccak256"
+// Warning: (99-109): "suicide" has been deprecated in favour of "selfdestruct"
diff --git a/test/libsolidity/syntaxTests/deprecated_functions_050.sol b/test/libsolidity/syntaxTests/deprecated_functions_050.sol
new file mode 100644
index 00000000..b28e5abb
--- /dev/null
+++ b/test/libsolidity/syntaxTests/deprecated_functions_050.sol
@@ -0,0 +1,15 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ bytes32 x = sha3(uint8(1));
+ x;
+ }
+ function g() public {
+ suicide(1);
+ }
+}
+// ----
+// TypeError: (88-102): "sha3" has been deprecated in favour of "keccak256"
+// TypeError: (88-102): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// TypeError: (88-102): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory.
+// TypeError: (137-147): "suicide" has been deprecated in favour of "selfdestruct"
diff --git a/test/libsolidity/syntaxTests/emit_non_event.sol b/test/libsolidity/syntaxTests/emit_non_event.sol
new file mode 100644
index 00000000..1df6990d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/emit_non_event.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint256 Test;
+
+ function f() {
+ emit Test();
+ }
+}
+// ----
+// TypeError: (56-62): Type is not callable
+// TypeError: (56-60): Expression has to be an event invocation.
diff --git a/test/libsolidity/syntaxTests/empty_string_var.sol b/test/libsolidity/syntaxTests/empty_string_var.sol
new file mode 100644
index 00000000..e9837590
--- /dev/null
+++ b/test/libsolidity/syntaxTests/empty_string_var.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() {
+ var a = "";
+ bytes1 b = bytes1(a);
+ bytes memory c = bytes(a);
+ string memory d = string(a);
+ }
+}
+// ----
+// Warning: (34-39): Use of the "var" keyword is deprecated.
+// TypeError: (61-70): Explicit type conversion not allowed from "string memory" to "bytes1".
diff --git a/test/libsolidity/syntaxTests/fallback/pure_modifier.sol b/test/libsolidity/syntaxTests/fallback/pure_modifier.sol
new file mode 100644
index 00000000..20d5b0ac
--- /dev/null
+++ b/test/libsolidity/syntaxTests/fallback/pure_modifier.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint x;
+ function() pure { x = 2; }
+}
+// ----
+// TypeError: (29-55): Fallback function must be payable or non-payable, but is "pure".
diff --git a/test/libsolidity/syntaxTests/fallback/view_modifier.sol b/test/libsolidity/syntaxTests/fallback/view_modifier.sol
new file mode 100644
index 00000000..44c5d204
--- /dev/null
+++ b/test/libsolidity/syntaxTests/fallback/view_modifier.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint x;
+ function() view { x = 2; }
+}
+// ----
+// TypeError: (29-55): Fallback function must be payable or non-payable, but is "view".
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol
index b7763d28..b89a3bb4 100644
--- a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol
@@ -5,4 +5,4 @@ contract C {
}
}
// ----
-// ParserError: (118-118): Expected token Semicolon got 'Identifier'
+// ParserError: (118-119): Expected ';' but got identifier
diff --git a/test/libsolidity/syntaxTests/inheritance/override/add_view.sol b/test/libsolidity/syntaxTests/inheritance/override/add_view.sol
new file mode 100644
index 00000000..9973b23e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/override/add_view.sol
@@ -0,0 +1,4 @@
+contract B { function f() public {} }
+contract C is B { function f() view {} }
+// ----
+// TypeError: (56-76): Overriding function changes state mutability from "nonpayable" to "view".
diff --git a/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol b/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol
new file mode 100644
index 00000000..e58f6b20
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol
@@ -0,0 +1,4 @@
+contract B { function f() view {} }
+contract C is B { function f() public {} }
+// ----
+// TypeError: (54-76): Overriding function changes state mutability from "view" to "nonpayable".
diff --git a/test/libsolidity/syntaxTests/missing_state_variable.sol b/test/libsolidity/syntaxTests/missing_state_variable.sol
new file mode 100644
index 00000000..02082a45
--- /dev/null
+++ b/test/libsolidity/syntaxTests/missing_state_variable.sol
@@ -0,0 +1,7 @@
+contract Scope {
+ function getStateVar() view public returns (uint stateVar) {
+ stateVar = Scope.stateVar; // should fail.
+ }
+}
+// ----
+// TypeError: (101-115): Member "stateVar" not found or not visible after argument-dependent lookup in type(contract Scope)
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol
new file mode 100644
index 00000000..182ba072
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol
@@ -0,0 +1,6 @@
+contract C {
+ function f() internal returns (uint) {
+ (uint a) = f();
+ a;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol
new file mode 100644
index 00000000..a3ce6a74
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol
@@ -0,0 +1,11 @@
+contract D {
+ struct S { uint a; uint b; }
+}
+contract C {
+ function f() internal returns (uint, uint, uint, D.S[20] storage, uint) {
+ (,,,D.S[10*2] storage x,) = f();
+ x;
+ }
+}
+// ----
+// Warning: (110-117): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol
new file mode 100644
index 00000000..c8686ae8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol
@@ -0,0 +1,8 @@
+contract C {
+ function f() internal returns (uint, uint, uint, uint) {
+ var (uint a, uint b,,) = f();
+ a; b;
+ }
+}
+// ----
+// ParserError: (81-85): Expected identifier but got 'uint'
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol
new file mode 100644
index 00000000..2b765837
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol
@@ -0,0 +1,9 @@
+contract C {
+ function f() internal returns (string memory, uint, uint, uint) {
+ (uint a, string memory b,,) = f();
+ a; b;
+ }
+}
+// ----
+// TypeError: (85-118): Type string memory is not implicitly convertible to expected type uint256.
+// TypeError: (85-118): Type uint256 is not implicitly convertible to expected type string memory.
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol
new file mode 100644
index 00000000..3ba85f69
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol
@@ -0,0 +1,12 @@
+pragma experimental "v0.5.0";
+
+contract C {
+ function f() internal {
+ {
+ (uint a, uint b, uint c) = (1, 2, 3);
+ }
+ a;
+ }
+}
+// ----
+// DeclarationError: (130-131): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol
new file mode 100644
index 00000000..e21181de
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol
@@ -0,0 +1,13 @@
+pragma experimental "v0.5.0";
+
+contract C {
+ function f() internal {
+ {
+ (uint a, uint b, uint c) = (a, b, c);
+ }
+ }
+}
+// ----
+// DeclarationError: (110-111): Undeclared identifier. Did you mean "a"?
+// DeclarationError: (113-114): Undeclared identifier. Did you mean "b"?
+// DeclarationError: (116-117): Undeclared identifier. Did you mean "c"?
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol
new file mode 100644
index 00000000..8e06322c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol
@@ -0,0 +1,12 @@
+contract C {
+ function f() internal returns (uint, uint, uint, uint) {
+ (uint a, uint b,,) = f();
+ a; b;
+ }
+ function g() internal returns (bytes memory, string storage) {
+ (bytes memory a, string storage b) = g();
+ a; b;
+ }
+}
+// ----
+// Warning: (163-169): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning.
diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol
new file mode 100644
index 00000000..8ae0eaac
--- /dev/null
+++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol
@@ -0,0 +1,9 @@
+contract C {
+ struct S { function() returns (S storage)[] x; }
+ S s;
+ function f() internal pure returns (uint, uint, uint, S storage, uint, uint) {
+ (,,,s.x[2](),,) = f();
+ }
+}
+// ----
+// TypeError: (160-168): Expression has to be an lvalue.
diff --git a/test/libsolidity/syntaxTests/docstring_empty_description.sol b/test/libsolidity/syntaxTests/natspec/docstring_empty_description.sol
index 0caa1b23..0caa1b23 100644
--- a/test/libsolidity/syntaxTests/docstring_empty_description.sol
+++ b/test/libsolidity/syntaxTests/natspec/docstring_empty_description.sol
diff --git a/test/libsolidity/syntaxTests/natspec/docstring_empty_tag.sol b/test/libsolidity/syntaxTests/natspec/docstring_empty_tag.sol
new file mode 100644
index 00000000..9a28143a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/natspec/docstring_empty_tag.sol
@@ -0,0 +1,6 @@
+contract C {
+ /// @param
+ function vote(uint id) public;
+}
+// ----
+// DocstringParsingError: End of tag @param not found
diff --git a/test/libsolidity/syntaxTests/parsing/comment_end_with_double_star.sol b/test/libsolidity/syntaxTests/parsing/comment_end_with_double_star.sol
new file mode 100644
index 00000000..d3fcae9b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/comment_end_with_double_star.sol
@@ -0,0 +1,5 @@
+contract C1 {
+/**
+ **/
+}
+contract C2 {}
diff --git a/test/libsolidity/syntaxTests/parsing/constant_is_keyword.sol b/test/libsolidity/syntaxTests/parsing/constant_is_keyword.sol
new file mode 100644
index 00000000..26d126ce
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/constant_is_keyword.sol
@@ -0,0 +1,5 @@
+contract Foo {
+ uint constant = 4;
+}
+// ----
+// ParserError: (30-31): Expected identifier but got '='
diff --git a/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol b/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol
new file mode 100644
index 00000000..da068351
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol
@@ -0,0 +1,7 @@
+contract C {
+ uint s;
+ // this test should fail starting from 0.5.0
+ function f() public constant returns (uint) {
+ return s;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/emit_without_event.sol b/test/libsolidity/syntaxTests/parsing/emit_without_event.sol
new file mode 100644
index 00000000..b838b4af
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/emit_without_event.sol
@@ -0,0 +1,8 @@
+contract C {
+ event A();
+ function f() {
+ emit A;
+ }
+}
+// ----
+// ParserError: (49-50): Expected '(' but got ';'
diff --git a/test/libsolidity/syntaxTests/parsing/empty_comment.sol b/test/libsolidity/syntaxTests/parsing/empty_comment.sol
new file mode 100644
index 00000000..b39c8483
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/empty_comment.sol
@@ -0,0 +1,3 @@
+//
+contract test
+{}
diff --git a/test/libsolidity/syntaxTests/parsing/empty_enum.sol b/test/libsolidity/syntaxTests/parsing/empty_enum.sol
new file mode 100644
index 00000000..483f6a91
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/empty_enum.sol
@@ -0,0 +1,5 @@
+contract c {
+ enum foo { }
+}
+// ----
+// ParserError: (25-26): enum with no members is not allowed.
diff --git a/test/libsolidity/syntaxTests/parsing/empty_function.sol b/test/libsolidity/syntaxTests/parsing/empty_function.sol
new file mode 100644
index 00000000..218fd9a7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/empty_function.sol
@@ -0,0 +1,10 @@
+contract test {
+ uint256 stateVar;
+ function functionName(bytes20 arg1, address addr) view returns (int id) { }
+}
+// ----
+// Warning: (36-111): No visibility specified. Defaulting to "public".
+// Warning: (58-70): Unused function parameter. Remove or comment out the variable name to silence this warning.
+// Warning: (72-84): Unused function parameter. Remove or comment out the variable name to silence this warning.
+// Warning: (100-106): Unused function parameter. Remove or comment out the variable name to silence this warning.
+// Warning: (36-111): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/event_with_no_argument_list.sol b/test/libsolidity/syntaxTests/parsing/event_with_no_argument_list.sol
new file mode 100644
index 00000000..b38c7dc9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/event_with_no_argument_list.sol
@@ -0,0 +1,5 @@
+contract c {
+ event e;
+}
+// ----
+// ParserError: (21-22): Expected '(' but got ';'
diff --git a/test/libsolidity/syntaxTests/parsing/external_variable.sol b/test/libsolidity/syntaxTests/parsing/external_variable.sol
new file mode 100644
index 00000000..2a02d19a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/external_variable.sol
@@ -0,0 +1,5 @@
+contract c {
+ uint external x;
+}
+// ----
+// ParserError: (19-27): Expected identifier but got 'external'
diff --git a/test/libsolidity/syntaxTests/parsing/fixed_literal_with_double_radix.sol b/test/libsolidity/syntaxTests/parsing/fixed_literal_with_double_radix.sol
new file mode 100644
index 00000000..0d5734aa
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/fixed_literal_with_double_radix.sol
@@ -0,0 +1,5 @@
+contract A {
+ fixed40x40 pi = 3.14.15;
+}
+// ----
+// ParserError: (34-37): Expected ';' but got 'Number'
diff --git a/test/libsolidity/syntaxTests/parsing/function_no_body.sol b/test/libsolidity/syntaxTests/parsing/function_no_body.sol
new file mode 100644
index 00000000..0424ebd8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/function_no_body.sol
@@ -0,0 +1,5 @@
+contract test {
+ function functionName(bytes32 input) returns (bytes32 out);
+}
+// ----
+// Warning: (17-76): No visibility specified. Defaulting to "public".
diff --git a/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_modifiers.sol b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_modifiers.sol
new file mode 100644
index 00000000..6c22f4b7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_modifiers.sol
@@ -0,0 +1,5 @@
+contract test {
+ function (uint, uint) modifier1() returns (uint) f1;
+}
+// ----
+// ParserError: (66-68): Expected '{' but got identifier
diff --git a/test/libsolidity/syntaxTests/parsing/function_type_state_variable.sol b/test/libsolidity/syntaxTests/parsing/function_type_state_variable.sol
new file mode 100644
index 00000000..eff52c7c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/function_type_state_variable.sol
@@ -0,0 +1,4 @@
+contract test {
+ function() x;
+ function() y = x;
+} \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_lvalue.sol b/test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_lvalue.sol
new file mode 100644
index 00000000..9460751c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_lvalue.sol
@@ -0,0 +1,9 @@
+contract c {
+ uint[] a;
+ function f() returns (uint) {
+ a = [,2,3];
+ return (a[0]);
+ }
+}
+// ----
+// ParserError: (62-63): Expected expression (inline array elements cannot be omitted).
diff --git a/test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_without_lvalue.sol b/test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_without_lvalue.sol
new file mode 100644
index 00000000..5b78232d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/inline_array_empty_cells_check_without_lvalue.sol
@@ -0,0 +1,8 @@
+contract c {
+ uint[] a;
+ function f() returns (uint, uint) {
+ return ([3, ,4][0]);
+ }
+}
+// ----
+// ParserError: (75-76): Expected expression (inline array elements cannot be omitted).
diff --git a/test/libsolidity/syntaxTests/parsing/interface_basic.sol b/test/libsolidity/syntaxTests/parsing/interface_basic.sol
new file mode 100644
index 00000000..c25b48ba
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/interface_basic.sol
@@ -0,0 +1,6 @@
+interface Interface {
+ function f();
+}
+// ----
+// Warning: (23-36): Functions in interfaces should be declared external.
+// Warning: (23-36): No visibility specified. Defaulting to "public". In interfaces it defaults to external.
diff --git a/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol b/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol
new file mode 100644
index 00000000..fb267ba3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/invalid_fixed_conversion_leading_zeroes_check.sol
@@ -0,0 +1,7 @@
+contract test {
+ function f() {
+ fixed a = 1.0x2;
+ }
+}
+// ----
+// ParserError: (44-47): Expected primary expression.
diff --git a/test/libsolidity/syntaxTests/parsing/local_const_variable.sol b/test/libsolidity/syntaxTests/parsing/local_const_variable.sol
new file mode 100644
index 00000000..505fe0b5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/local_const_variable.sol
@@ -0,0 +1,9 @@
+contract Foo {
+ function localConst() returns (uint ret)
+ {
+ uint constant local = 4;
+ return local;
+ }
+}
+// ----
+// ParserError: (67-75): Expected ';' but got 'constant'
diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables.sol
new file mode 100644
index 00000000..40eaf838
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables.sol
@@ -0,0 +1,5 @@
+contract Foo {
+ uint[] memory x;
+}
+// ----
+// ParserError: (23-29): Expected identifier but got 'memory'
diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_with_var.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_with_var.sol
new file mode 100644
index 00000000..2b8f08c5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_with_var.sol
@@ -0,0 +1,5 @@
+contract Foo {
+ function f() { var memory x; }
+}
+// ----
+// ParserError: (35-41): Location specifier needs explicit type name.
diff --git a/test/libsolidity/syntaxTests/parsing/malformed_enum_declaration.sol b/test/libsolidity/syntaxTests/parsing/malformed_enum_declaration.sol
new file mode 100644
index 00000000..811b4219
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/malformed_enum_declaration.sol
@@ -0,0 +1,5 @@
+contract c {
+ enum foo { WARNING,}
+}
+// ----
+// ParserError: (33-34): Expected identifier after ','
diff --git a/test/libsolidity/syntaxTests/parsing/missing_argument_in_named_args.sol b/test/libsolidity/syntaxTests/parsing/missing_argument_in_named_args.sol
new file mode 100644
index 00000000..f7c3fb31
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/missing_argument_in_named_args.sol
@@ -0,0 +1,6 @@
+contract test {
+ function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
+ function b() returns (uint r) { r = a({a: , b: , c: }); }
+}
+// ----
+// ParserError: (146-147): Expected primary expression.
diff --git a/test/libsolidity/syntaxTests/parsing/missing_parameter_name_in_named_args.sol b/test/libsolidity/syntaxTests/parsing/missing_parameter_name_in_named_args.sol
new file mode 100644
index 00000000..d7c2f2be
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/missing_parameter_name_in_named_args.sol
@@ -0,0 +1,6 @@
+contract test {
+ function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
+ function b() returns (uint r) { r = a({: 1, : 2, : 3}); }
+}
+// ----
+// ParserError: (143-144): Expected identifier but got ':'
diff --git a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
index fd3067e3..51b7bd24 100644
--- a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
+++ b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
@@ -2,4 +2,4 @@ contract test {
uint256 ;
}
// ----
-// ParserError: (28-28): Expected identifier, got 'Semicolon'
+// ParserError: (28-29): Expected identifier but got ';'
diff --git a/test/libsolidity/syntaxTests/parsing/modifier_without_semicolon.sol b/test/libsolidity/syntaxTests/parsing/modifier_without_semicolon.sol
new file mode 100644
index 00000000..a9fa33e6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/modifier_without_semicolon.sol
@@ -0,0 +1,5 @@
+contract c {
+ modifier mod { if (msg.sender == 0) _ }
+}
+// ----
+// ParserError: (52-53): Expected ';' but got '}'
diff --git a/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol b/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol
new file mode 100644
index 00000000..818999df
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol
@@ -0,0 +1,29 @@
+contract C {
+ function f() {
+ var (a,b,c) = g();
+ var (d) = 2;
+ var (,e) = 3;
+ var (f,) = 4;
+ var (x,,) = g();
+ var (,y,) = g();
+ var () = g();
+ var (,,) = g();
+ }
+ function g() returns (uint, uint, uint) {}
+}
+// ----
+// Warning: (36-37): Use of the "var" keyword is deprecated.
+// Warning: (38-39): Use of the "var" keyword is deprecated.
+// Warning: (40-41): Use of the "var" keyword is deprecated.
+// Warning: (57-58): Use of the "var" keyword is deprecated.
+// Warning: (73-74): Use of the "var" keyword is deprecated.
+// Warning: (88-89): Use of the "var" keyword is deprecated.
+// Warning: (104-105): Use of the "var" keyword is deprecated.
+// Warning: (124-125): Use of the "var" keyword is deprecated.
+// Warning: (88-89): This declaration shadows an existing declaration.
+// Warning: (52-63): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (67-79): Different number of components on the left hand side (2) than on the right hand side (1).
+// Warning: (67-79): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (83-95): Different number of components on the left hand side (2) than on the right hand side (1).
+// Warning: (83-95): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning.
+// TypeError: (137-149): Too many components (3) in value for variable assignment (0) needed
diff --git a/test/libsolidity/syntaxTests/parsing/multiple_function_param_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/multiple_function_param_trailing_comma.sol
new file mode 100644
index 00000000..ae65b33c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/multiple_function_param_trailing_comma.sol
@@ -0,0 +1,5 @@
+contract test {
+ function(uint a, uint b,) {}
+}
+// ----
+// ParserError: (40-41): Unexpected trailing comma in parameter list.
diff --git a/test/libsolidity/syntaxTests/parsing/multiple_statemutability_specifiers.sol b/test/libsolidity/syntaxTests/parsing/multiple_statemutability_specifiers.sol
new file mode 100644
index 00000000..a05bf452
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/multiple_statemutability_specifiers.sol
@@ -0,0 +1,33 @@
+contract c1 {
+ function f() payable payable {}
+}
+contract c2 {
+ function f() view view {}
+}
+contract c3 {
+ function f() pure pure {}
+}
+contract c4 {
+ function f() pure view {}
+}
+contract c5 {
+ function f() payable view {}
+}
+contract c6 {
+ function f() pure payable {}
+}
+contract c7 {
+ function f() pure constant {}
+}
+contract c8 {
+ function f() view constant {}
+}
+// ----
+// ParserError: (39-46): State mutability already specified as "payable".
+// ParserError: (88-92): State mutability already specified as "view".
+// ParserError: (134-138): State mutability already specified as "pure".
+// ParserError: (180-184): State mutability already specified as "pure".
+// ParserError: (229-233): State mutability already specified as "payable".
+// ParserError: (275-282): State mutability already specified as "pure".
+// ParserError: (324-332): State mutability already specified as "pure".
+// ParserError: (374-382): State mutability already specified as "view".
diff --git a/test/libsolidity/syntaxTests/parsing/new_invalid_type_name.sol b/test/libsolidity/syntaxTests/parsing/new_invalid_type_name.sol
new file mode 100644
index 00000000..d469bc75
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/new_invalid_type_name.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() {
+ new var;
+ }
+}
+// ----
+// ParserError: (35-38): Expected explicit type name.
diff --git a/test/libsolidity/syntaxTests/parsing/no_function_params.sol b/test/libsolidity/syntaxTests/parsing/no_function_params.sol
new file mode 100644
index 00000000..020f1233
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/no_function_params.sol
@@ -0,0 +1,7 @@
+contract test {
+ uint256 stateVar;
+ function functionName() {}
+}
+// ----
+// Warning: (36-62): No visibility specified. Defaulting to "public".
+// Warning: (36-62): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol b/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol
new file mode 100644
index 00000000..1a78d155
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol
@@ -0,0 +1,9 @@
+contract test {
+ function fun(uint a) returns(uint r) { return a; }
+ function fun(uint a, uint b) returns(uint r) { return a + b; }
+}
+// ----
+// Warning: (17-67): No visibility specified. Defaulting to "public".
+// Warning: (69-131): No visibility specified. Defaulting to "public".
+// Warning: (17-67): Function state mutability can be restricted to pure
+// Warning: (69-131): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/payable_accessor.sol b/test/libsolidity/syntaxTests/parsing/payable_accessor.sol
new file mode 100644
index 00000000..6504004b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/payable_accessor.sol
@@ -0,0 +1,5 @@
+contract test {
+ uint payable x;
+}
+// ----
+// ParserError: (22-29): Expected identifier but got 'payable'
diff --git a/test/libsolidity/syntaxTests/parsing/return_var.sol b/test/libsolidity/syntaxTests/parsing/return_var.sol
index 47ac9ef0..b9ce8f95 100644
--- a/test/libsolidity/syntaxTests/parsing/return_var.sol
+++ b/test/libsolidity/syntaxTests/parsing/return_var.sol
@@ -9,17 +9,17 @@ contract C {
function f() public pure returns (var storage x, var storage y) {}
}
// ----
-// ParserError: (38-38): Expected explicit type name.
-// ParserError: (71-71): Expected explicit type name.
-// ParserError: (106-106): Expected explicit type name.
-// ParserError: (157-157): Expected explicit type name.
-// ParserError: (192-192): Expected explicit type name.
-// ParserError: (199-199): Expected explicit type name.
-// ParserError: (247-247): Expected explicit type name.
-// ParserError: (251-251): Location specifier needs explicit type name.
-// ParserError: (301-301): Expected explicit type name.
-// ParserError: (305-305): Location specifier needs explicit type name.
-// ParserError: (357-357): Expected explicit type name.
-// ParserError: (361-361): Location specifier needs explicit type name.
-// ParserError: (372-372): Expected explicit type name.
-// ParserError: (376-376): Location specifier needs explicit type name.
+// ParserError: (38-41): Expected explicit type name.
+// ParserError: (71-74): Expected explicit type name.
+// ParserError: (106-109): Expected explicit type name.
+// ParserError: (157-160): Expected explicit type name.
+// ParserError: (192-195): Expected explicit type name.
+// ParserError: (199-202): Expected explicit type name.
+// ParserError: (247-250): Expected explicit type name.
+// ParserError: (251-258): Location specifier needs explicit type name.
+// ParserError: (301-304): Expected explicit type name.
+// ParserError: (305-312): Location specifier needs explicit type name.
+// ParserError: (357-360): Expected explicit type name.
+// ParserError: (361-368): Location specifier needs explicit type name.
+// ParserError: (372-375): Expected explicit type name.
+// ParserError: (376-383): Location specifier needs explicit type name.
diff --git a/test/libsolidity/syntaxTests/parsing/scientific_notation.sol b/test/libsolidity/syntaxTests/parsing/scientific_notation.sol
new file mode 100644
index 00000000..5d656508
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/scientific_notation.sol
@@ -0,0 +1,7 @@
+contract test {
+ uint256 a = 2e10;
+ uint256 b = 2E10;
+ uint256 c = 200e-2;
+ uint256 d = 2E10 wei;
+ uint256 e = 2.5e10;
+}
diff --git a/test/libsolidity/syntaxTests/parsing/single_event_arg_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/single_event_arg_trailing_comma.sol
new file mode 100644
index 00000000..50638dc7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/single_event_arg_trailing_comma.sol
@@ -0,0 +1,6 @@
+contract test {
+ event Test(uint a,);
+ function(uint a) {}
+}
+// ----
+// ParserError: (34-35): Unexpected trailing comma in parameter list.
diff --git a/test/libsolidity/syntaxTests/parsing/single_function_param.sol b/test/libsolidity/syntaxTests/parsing/single_function_param.sol
new file mode 100644
index 00000000..08e531f1
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/single_function_param.sol
@@ -0,0 +1,9 @@
+contract test {
+ uint256 stateVar;
+ function functionName(bytes32 input) returns (bytes32 out) {}
+}
+// ----
+// Warning: (36-97): No visibility specified. Defaulting to "public".
+// Warning: (58-71): Unused function parameter. Remove or comment out the variable name to silence this warning.
+// Warning: (82-93): Unused function parameter. Remove or comment out the variable name to silence this warning.
+// Warning: (36-97): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/single_function_param_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/single_function_param_trailing_comma.sol
new file mode 100644
index 00000000..fe52b3b2
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/single_function_param_trailing_comma.sol
@@ -0,0 +1,5 @@
+contract test {
+ function(uint a,) {}
+}
+// ----
+// ParserError: (32-33): Unexpected trailing comma in parameter list.
diff --git a/test/libsolidity/syntaxTests/parsing/single_modifier_arg_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/single_modifier_arg_trailing_comma.sol
new file mode 100644
index 00000000..2f60405f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/single_modifier_arg_trailing_comma.sol
@@ -0,0 +1,6 @@
+contract test {
+ modifier modTest(uint a,) { _; }
+ function(uint a) {}
+}
+// ----
+// ParserError: (40-41): Unexpected trailing comma in parameter list.
diff --git a/test/libsolidity/syntaxTests/parsing/single_return_param_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/single_return_param_trailing_comma.sol
new file mode 100644
index 00000000..99f91819
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/single_return_param_trailing_comma.sol
@@ -0,0 +1,5 @@
+contract test {
+ function() returns (uint a,) {}
+}
+// ----
+// ParserError: (43-44): Unexpected trailing comma in parameter list.
diff --git a/test/libsolidity/syntaxTests/parsing/trailing_comma_in_named_args.sol b/test/libsolidity/syntaxTests/parsing/trailing_comma_in_named_args.sol
new file mode 100644
index 00000000..8fc3dab8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/trailing_comma_in_named_args.sol
@@ -0,0 +1,6 @@
+contract test {
+ function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; }
+ function b() returns (uint r) { r = a({a: 1, b: 2, c: 3, }); }
+}
+// ----
+// ParserError: (159-160): Unexpected trailing comma.
diff --git a/test/libsolidity/syntaxTests/parsing/tuples.sol b/test/libsolidity/syntaxTests/parsing/tuples.sol
new file mode 100644
index 00000000..6f739740
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/tuples.sol
@@ -0,0 +1,24 @@
+contract C {
+ function f() {
+ uint a = (1);
+ var (b,) = (1,);
+ var (c,d) = (1, 2 + a);
+ var (e,) = (1, 2, b);
+ (a) = 3;
+ }
+}
+// ----
+// Warning: (52-53): Use of the "var" keyword is deprecated.
+// Warning: (71-72): Use of the "var" keyword is deprecated.
+// Warning: (73-74): Use of the "var" keyword is deprecated.
+// Warning: (97-98): Use of the "var" keyword is deprecated.
+// Warning: (47-62): Different number of components on the left hand side (2) than on the right hand side (1).
+// Warning: (47-62): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (66-88): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (92-112): Different number of components on the left hand side (2) than on the right hand side (3).
+// Warning: (92-112): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (14-127): No visibility specified. Defaulting to "public".
+// Warning: (71-72): Unused local variable.
+// Warning: (73-74): Unused local variable.
+// Warning: (97-98): Unused local variable.
+// Warning: (14-127): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/tuples_without_commas.sol b/test/libsolidity/syntaxTests/parsing/tuples_without_commas.sol
new file mode 100644
index 00000000..fcbb875c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/tuples_without_commas.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() {
+ var a = (2 2);
+ }
+}
+// ----
+// ParserError: (42-43): Expected ',' but got 'Number'
diff --git a/test/libsolidity/syntaxTests/parsing/var_array.sol b/test/libsolidity/syntaxTests/parsing/var_array.sol
new file mode 100644
index 00000000..e08f5511
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/var_array.sol
@@ -0,0 +1,5 @@
+contract Foo {
+ function f() { var[] a; }
+}
+// ----
+// ParserError: (34-35): Expected identifier but got '['
diff --git a/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol
index e041247d..2e5a9c58 100644
--- a/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol
+++ b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol
@@ -9,17 +9,17 @@ contract C {
function f(var storage x, var storage y) public pure {}
}
// ----
-// ParserError: (28-28): Expected explicit type name.
-// ParserError: (63-63): Expected explicit type name.
-// ParserError: (100-100): Expected explicit type name.
-// ParserError: (107-107): Expected explicit type name.
-// ParserError: (152-152): Expected explicit type name.
-// ParserError: (189-189): Expected explicit type name.
-// ParserError: (234-234): Expected explicit type name.
-// ParserError: (238-238): Location specifier needs explicit type name.
-// ParserError: (277-277): Expected explicit type name.
-// ParserError: (281-281): Location specifier needs explicit type name.
-// ParserError: (322-322): Expected explicit type name.
-// ParserError: (326-326): Location specifier needs explicit type name.
-// ParserError: (337-337): Expected explicit type name.
-// ParserError: (341-341): Location specifier needs explicit type name.
+// ParserError: (28-31): Expected explicit type name.
+// ParserError: (63-66): Expected explicit type name.
+// ParserError: (100-103): Expected explicit type name.
+// ParserError: (107-110): Expected explicit type name.
+// ParserError: (152-155): Expected explicit type name.
+// ParserError: (189-192): Expected explicit type name.
+// ParserError: (234-237): Expected explicit type name.
+// ParserError: (238-245): Location specifier needs explicit type name.
+// ParserError: (277-280): Expected explicit type name.
+// ParserError: (281-288): Location specifier needs explicit type name.
+// ParserError: (322-325): Expected explicit type name.
+// ParserError: (326-333): Location specifier needs explicit type name.
+// ParserError: (337-340): Expected explicit type name.
+// ParserError: (341-348): Location specifier needs explicit type name.
diff --git a/test/libsolidity/syntaxTests/parsing/var_storage_var.sol b/test/libsolidity/syntaxTests/parsing/var_storage_var.sol
index 431d4ca5..6e4ccfa5 100644
--- a/test/libsolidity/syntaxTests/parsing/var_storage_var.sol
+++ b/test/libsolidity/syntaxTests/parsing/var_storage_var.sol
@@ -2,4 +2,4 @@ contract C {
var a;
}
// ----
-// ParserError: (17-17): Function, variable, struct or modifier declaration expected.
+// ParserError: (17-20): Function, variable, struct or modifier declaration expected.
diff --git a/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol b/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol
new file mode 100644
index 00000000..61f5be53
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/variable_definition_in_mapping.sol
@@ -0,0 +1,7 @@
+contract test {
+ function fun() {
+ mapping(var=>bytes32) d;
+ }
+}
+// ----
+// ParserError: (44-47): Expected elementary type name for mapping key type
diff --git a/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol b/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol
new file mode 100644
index 00000000..a6ee4bf1
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol
@@ -0,0 +1,15 @@
+contract C {
+ function f() pure public {
+ g(keccak256(uint(2)));
+ g(sha256(uint(2)));
+ g(ripemd160(uint(2)));
+ }
+ function g(bytes32) pure internal {}
+}
+// ----
+// Warning: (54-72): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (54-72): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory.
+// Warning: (85-100): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (85-100): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory.
+// Warning: (113-131): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (113-131): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol
index c98d7a57..b94a4391 100644
--- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol
@@ -1,11 +1,11 @@
contract C {
function f() public pure {
- bytes32 h = keccak256(keccak256, f, this.f.gas, block.blockhash);
+ bytes32 h = keccak256(abi.encodePacked(keccak256, f, this.f.gas, block.blockhash));
h;
}
}
// ----
-// TypeError: (74-83): This type cannot be encoded.
-// TypeError: (85-86): This type cannot be encoded.
-// TypeError: (88-98): This type cannot be encoded.
-// TypeError: (100-115): This type cannot be encoded.
+// TypeError: (91-100): This type cannot be encoded.
+// TypeError: (102-103): This type cannot be encoded.
+// TypeError: (105-115): This type cannot be encoded.
+// TypeError: (117-132): This type cannot be encoded.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol
index fa910260..05f5db0b 100644
--- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol
@@ -9,5 +9,6 @@ contract C {
}
}
// ----
+// Warning: (132-144): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
// TypeError: (139-140): This type cannot be encoded.
// TypeError: (142-143): This type cannot be encoded.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol
index 1187ce4a..977a7d73 100644
--- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol
@@ -12,5 +12,6 @@ contract C {
}
// ----
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// Warning: (167-179): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
// TypeError: (174-175): This type cannot be encoded.
// TypeError: (177-178): This type cannot be encoded.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol
index c8364548..d890e35f 100644
--- a/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol
@@ -2,7 +2,7 @@ contract C {
uint[3] sarr;
function f() view public {
uint[3] memory arr;
- bytes32 h = keccak256(this.f, arr, sarr);
+ bytes32 h = keccak256(abi.encodePacked(this.f, arr, sarr));
h;
}
}
diff --git a/test/libsolidity/syntaxTests/tight_packing_literals.sol b/test/libsolidity/syntaxTests/tight_packing_literals.sol
index 8258a8a6..a190adc3 100644
--- a/test/libsolidity/syntaxTests/tight_packing_literals.sol
+++ b/test/libsolidity/syntaxTests/tight_packing_literals.sol
@@ -18,8 +18,16 @@ contract C {
// ----
// Warning: (87-88): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (77-89): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (77-89): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
// Warning: (161-168): "sha3" has been deprecated in favour of "keccak256"
// Warning: (166-167): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (161-168): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (161-168): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
// Warning: (247-248): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (240-249): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (240-249): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
// Warning: (331-332): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning.
+// Warning: (321-333): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (321-333): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
// Warning: (420-421): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning.
diff --git a/test/libsolidity/syntaxTests/tight_packing_literals_050.sol b/test/libsolidity/syntaxTests/tight_packing_literals_050.sol
new file mode 100644
index 00000000..b7557d2a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tight_packing_literals_050.sol
@@ -0,0 +1,34 @@
+pragma experimental "v0.5.0";
+contract C {
+ function f() pure public returns (bytes32) {
+ return keccak256(1);
+ }
+ function g() pure public returns (bytes32) {
+ return sha3(1);
+ }
+ function h() pure public returns (bytes32) {
+ return sha256(1);
+ }
+ function j() pure public returns (bytes32) {
+ return ripemd160(1);
+ }
+ function k() pure public returns (bytes) {
+ return abi.encodePacked(1);
+ }
+}
+
+// ----
+// TypeError: (117-118): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
+// TypeError: (107-119): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// TypeError: (107-119): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
+// TypeError: (191-198): "sha3" has been deprecated in favour of "keccak256"
+// TypeError: (196-197): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
+// TypeError: (191-198): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// TypeError: (191-198): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
+// TypeError: (277-278): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
+// TypeError: (270-279): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// TypeError: (270-279): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
+// TypeError: (361-362): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
+// TypeError: (351-363): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// TypeError: (351-363): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory.
+// TypeError: (450-451): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
diff --git a/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol
index 46407f71..2b9b688a 100644
--- a/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol
+++ b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol
@@ -2,10 +2,29 @@ contract C {
function f() pure public returns (bytes32) {
return keccak256(uint8(1));
}
- function g() pure public returns (bytes) {
- return abi.encode(1);
+ function g() pure public returns (bytes32) {
+ return sha3(uint8(1));
+ }
+ function h() pure public returns (bytes32) {
+ return sha256(uint8(1));
+ }
+ function j() pure public returns (bytes32) {
+ return ripemd160(uint8(1));
}
- function h() pure public returns (bytes) {
+ function k() pure public returns (bytes) {
return abi.encodePacked(uint8(1));
}
+ function l() pure public returns (bytes) {
+ return abi.encode(1);
+ }
}
+// ----
+// Warning: (77-96): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (77-96): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory.
+// Warning: (168-182): "sha3" has been deprecated in favour of "keccak256"
+// Warning: (168-182): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (168-182): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory.
+// Warning: (254-270): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (254-270): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory.
+// Warning: (342-361): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data.
+// Warning: (342-361): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory.
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol b/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol
new file mode 100644
index 00000000..5b7f870b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol
@@ -0,0 +1,12 @@
+pragma experimental "v0.5.0";
+contract C {
+ function f() public pure returns (uint, uint, bytes32) {
+ uint a;
+ bytes32 b;
+ (a,) = f();
+ (,b) = f();
+ }
+}
+// ----
+// TypeError: (126-136): Different number of components on the left hand side (2) than on the right hand side (3).
+// TypeError: (140-150): Different number of components on the left hand side (2) than on the right hand side (3).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol
new file mode 100644
index 00000000..bbf21d7e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol
@@ -0,0 +1,24 @@
+pragma experimental "v0.5.0";
+contract C {
+ function g() public pure returns (
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint,
+ uint
+ ) { }
+ function f() public pure returns (uint, uint, bytes32) {
+ uint a;
+ uint b;
+ (,,,,a,,,,b,,,,) = g();
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol
new file mode 100644
index 00000000..3262781b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol
@@ -0,0 +1,8 @@
+contract C {
+ function f() public pure {
+ uint a;
+ (a,) = (uint(1),);
+ }
+}
+// ----
+// Warning: (53-70): Different number of components on the left hand side (2) than on the right hand side (1).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol
new file mode 100644
index 00000000..b20bbf90
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_memory.sol
@@ -0,0 +1,8 @@
+contract C {
+ struct S { uint a; uint b; }
+ function f() pure public {
+ S memory x;
+ S memory y;
+ (x, y) = (y, x);
+ }
+}
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol
new file mode 100644
index 00000000..5f7a18f7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_swap_storage_pointers.sol
@@ -0,0 +1,10 @@
+ contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ S storage x_local = x;
+ S storage y_local = y;
+ S storage z_local = x;
+ (x, y_local, x_local, z_local) = (y, x_local, y_local, y);
+ }
+ }
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol
new file mode 100644
index 00000000..a079a509
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() public pure returns (uint, uint, bytes32) {
+ uint a;
+ bytes32 b;
+ (a,) = f();
+ (,b) = f();
+ }
+}
+// ----
+// Warning: (96-106): Different number of components on the left hand side (2) than on the right hand side (3).
+// Warning: (110-120): Different number of components on the left hand side (2) than on the right hand side (3).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol
new file mode 100644
index 00000000..1d243c7c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() public pure returns (uint, uint, uint, uint) {
+ // Can later be replaced by (uint a, uint b,) = f();
+ var (a,b,) = f();
+ a; b;
+ }
+}
+// ----
+// Warning: (136-137): Use of the "var" keyword is deprecated.
+// Warning: (138-139): Use of the "var" keyword is deprecated.
+// Warning: (131-147): Different number of components on the left hand side (3) than on the right hand side (4).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol
new file mode 100644
index 00000000..e4c3e694
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies.sol
@@ -0,0 +1,9 @@
+contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ (x, y) = (y, x);
+ }
+}
+// ----
+// Warning: (79-94): 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 is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol
new file mode 100644
index 00000000..b2979804
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol
@@ -0,0 +1,10 @@
+contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ (,x, y) = (1, 2, y, x);
+ }
+}
+// ----
+// Warning: (79-101): 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 is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
+// Warning: (79-101): Different number of components on the left hand side (3) than on the right hand side (4).
diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol
new file mode 100644
index 00000000..aa35d7d4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol
@@ -0,0 +1,10 @@
+contract C {
+ struct S { uint a; uint b; }
+ S x; S y;
+ function f() public {
+ (x, y, ) = (y, x, 1, 2);
+ }
+}
+// ----
+// Warning: (79-102): 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 is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first.
+// Warning: (79-102): Different number of components on the left hand side (3) than on the right hand side (4).
diff --git a/test/libsolidity/syntaxTests/types/bytes0.sol b/test/libsolidity/syntaxTests/types/bytes0.sol
new file mode 100644
index 00000000..7c6d5974
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/bytes0.sol
@@ -0,0 +1,5 @@
+contract C {
+ bytes0 b0 = 1;
+}
+// ----
+// DeclarationError: (15-21): Identifier not found or not unique.
diff --git a/test/libsolidity/syntaxTests/types/bytes256.sol b/test/libsolidity/syntaxTests/types/bytes256.sol
new file mode 100644
index 00000000..22b5408d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/bytes256.sol
@@ -0,0 +1,5 @@
+contract C {
+ bytes256 b256 = 1;
+}
+// ----
+// DeclarationError: (15-23): Identifier not found or not unique.
diff --git a/test/libsolidity/syntaxTests/types/bytes33.sol b/test/libsolidity/syntaxTests/types/bytes33.sol
new file mode 100644
index 00000000..7edf13d3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/bytes33.sol
@@ -0,0 +1,5 @@
+contract C {
+ bytes33 b33 = 1;
+}
+// ----
+// DeclarationError: (15-22): Identifier not found or not unique.
diff --git a/test/libsolidity/syntaxTests/types/bytesm.sol b/test/libsolidity/syntaxTests/types/bytesm.sol
new file mode 100644
index 00000000..550760b9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/bytesm.sol
@@ -0,0 +1,36 @@
+contract C {
+ byte b = byte(1);
+ bytes1 b1 = b;
+ bytes2 b2 = b1;
+ bytes3 b3 = b2;
+ bytes4 b4 = b3;
+ bytes5 b5 = b4;
+ bytes6 b6 = b5;
+ bytes7 b7 = b6;
+ bytes8 b8 = b7;
+ bytes9 b9 = b8;
+ bytes10 b10 = b9;
+ bytes11 b11 = b10;
+ bytes12 b12 = b11;
+ bytes13 b13 = b12;
+ bytes14 b14 = b13;
+ bytes15 b15 = b14;
+ bytes16 b16 = b15;
+ bytes17 b17 = b16;
+ bytes18 b18 = b17;
+ bytes19 b19 = b18;
+ bytes20 b20 = b19;
+ bytes21 b21 = b20;
+ bytes22 b22 = b21;
+ bytes23 b23 = b22;
+ bytes24 b24 = b23;
+ bytes25 b25 = b24;
+ bytes26 b26 = b25;
+ bytes27 b27 = b26;
+ bytes28 b28 = b27;
+ bytes29 b29 = b28;
+ bytes30 b30 = b29;
+ bytes31 b31 = b30;
+ bytes32 b32 = b31;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_event.sol b/test/libsolidity/syntaxTests/types/empty_tuple_event.sol
new file mode 100644
index 00000000..3e40b155
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_event.sol
@@ -0,0 +1,10 @@
+pragma solidity ^0.4.3;
+contract C {
+ event SomeEvent();
+ function a() public {
+ (SomeEvent(), 7);
+ }
+}
+// ----
+// Warning: (95-106): Invoking events without "emit" prefix is deprecated.
+// Warning: (95-106): Tuple component cannot be empty.
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol b/test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol
new file mode 100644
index 00000000..aec5ff2a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol
@@ -0,0 +1,10 @@
+pragma experimental "v0.5.0";
+contract C {
+ event SomeEvent();
+ function a() public {
+ (SomeEvent(), 7);
+ }
+}
+// ----
+// TypeError: (101-112): Event invocations have to be prefixed by "emit".
+// TypeError: (101-112): Tuple component cannot be empty.
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_function.sol b/test/libsolidity/syntaxTests/types/empty_tuple_function.sol
new file mode 100644
index 00000000..05b54442
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_function.sol
@@ -0,0 +1,12 @@
+pragma solidity ^0.4.3;
+contract C {
+ function f() private pure {}
+ function a() public pure {
+ bool x = true;
+ bool y = true;
+ (x) ? (f(), y = false) : (f(), y = false);
+ }
+}
+// ----
+// Warning: (162-165): Tuple component cannot be empty.
+// Warning: (181-184): Tuple component cannot be empty.
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol b/test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol
new file mode 100644
index 00000000..c4b9e03f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol
@@ -0,0 +1,11 @@
+pragma experimental "v0.5.0";
+contract C {
+ function f() private pure {}
+ function a() public pure {
+ bool x = true;
+ bool y = true;
+ (x) ? (f(), y = false) : (f(), y = false);
+ }
+}
+// ----
+// TypeError: (168-171): Tuple component cannot be empty.
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol
new file mode 100644
index 00000000..cba30c1b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol
@@ -0,0 +1,13 @@
+pragma solidity ^0.4.3;
+contract C {
+ function f() private pure {}
+ function a() public {
+ uint x;
+ uint y;
+ (x, y) = (f(), f());
+ }
+}
+// ----
+// Warning: (146-149): Tuple component cannot be empty.
+// Warning: (151-154): Tuple component cannot be empty.
+// TypeError: (145-155): Type tuple(tuple(),tuple()) is not implicitly convertible to expected type tuple(uint256,uint256).
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol
new file mode 100644
index 00000000..b0691778
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol
@@ -0,0 +1,11 @@
+pragma experimental "v0.5.0";
+contract C {
+ function f() private pure {}
+ function a() public {
+ uint x;
+ uint y;
+ (x, y) = (f(), f());
+ }
+}
+// ----
+// TypeError: (152-155): Tuple component cannot be empty.
diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol
new file mode 100644
index 00000000..f8b2ae7e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol
@@ -0,0 +1,11 @@
+pragma solidity ^0.4.3;
+contract C {
+ function f() private pure {}
+ function a() public {
+ uint x;
+ uint y;
+ (x, y) = [f(), f()];
+ }
+}
+// ----
+// TypeError: (146-149): Array component cannot be empty.
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index febb0c26..11714017 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -1,5 +1,5 @@
add_executable(solfuzzer fuzzer.cpp)
-target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES})
+target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES})
add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp)
target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})