aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-04-17 05:03:49 +0800
committerGitHub <noreply@github.com>2018-04-17 05:03:49 +0800
commit4cb486ee993cadde5564fb6c611d2bcf4fc44414 (patch)
tree6b971913021037cf28141c38af97c7ecda77719f
parentdfe3193c7382c80f1814247a162663a97c3f5e67 (diff)
parentb8431c5c4a6795db8c42a1a3389047658bb87301 (diff)
downloaddexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.tar
dexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.tar.gz
dexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.tar.bz2
dexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.tar.lz
dexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.tar.xz
dexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.tar.zst
dexon-solidity-4cb486ee993cadde5564fb6c611d2bcf4fc44414.zip
Merge pull request #3892 from ethereum/develop
Merge develop into release for 0.4.22
-rw-r--r--.travis.yml8
-rw-r--r--CMakeLists.txt2
-rw-r--r--CODING_STYLE.md262
-rw-r--r--Changelog.md53
-rw-r--r--README.md2
-rw-r--r--ReleaseChecklist.md1
-rw-r--r--appveyor.yml2
-rw-r--r--circle.yml40
-rw-r--r--cmake/EthDependencies.cmake7
-rw-r--r--docs/abi-spec.rst8
-rw-r--r--docs/assembly.rst15
-rw-r--r--docs/bugs_by_version.json4
-rw-r--r--docs/common-patterns.rst25
-rw-r--r--docs/contracts.rst66
-rw-r--r--docs/contributing.rst149
-rw-r--r--docs/control-structures.rst54
-rw-r--r--docs/grammar.txt2
-rw-r--r--docs/index.rst1
-rw-r--r--docs/installing-solidity.rst1
-rw-r--r--docs/introduction-to-smart-contracts.rst8
-rw-r--r--docs/miscellaneous.rst40
-rw-r--r--docs/solidity-by-example.rst75
-rw-r--r--docs/structure-of-a-contract.rst7
-rw-r--r--docs/style-guide.rst2
-rw-r--r--docs/types.rst13
-rw-r--r--docs/units-and-global-variables.rst42
-rw-r--r--libdevcore/Algorithms.h76
-rw-r--r--libdevcore/CMakeLists.txt6
-rw-r--r--libdevcore/CommonIO.cpp69
-rw-r--r--libdevcore/CommonIO.h8
-rw-r--r--libevmasm/Assembly.cpp4
-rw-r--r--libevmasm/CommonSubexpressionEliminator.h8
-rw-r--r--libevmasm/Instruction.cpp2
-rw-r--r--libevmasm/PeepholeOptimiser.cpp47
-rw-r--r--libevmasm/RuleList.h10
-rw-r--r--libevmasm/SemanticInformation.cpp7
-rw-r--r--libevmasm/SemanticInformation.h3
-rw-r--r--libjulia/optimiser/CommonSubexpressionEliminator.cpp48
-rw-r--r--libjulia/optimiser/CommonSubexpressionEliminator.h45
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp6
-rw-r--r--libsolidity/analysis/ConstantEvaluator.h1
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp8
-rw-r--r--libsolidity/analysis/GlobalContext.cpp4
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp9
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp36
-rw-r--r--libsolidity/analysis/PostTypeChecker.h5
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp111
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h6
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp37
-rw-r--r--libsolidity/analysis/SyntaxChecker.h2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp253
-rw-r--r--libsolidity/analysis/TypeChecker.h7
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp7
-rw-r--r--libsolidity/ast/AST.cpp14
-rw-r--r--libsolidity/ast/AST.h41
-rw-r--r--libsolidity/ast/ASTAnnotations.h3
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp20
-rw-r--r--libsolidity/ast/ASTJsonConverter.h2
-rw-r--r--libsolidity/ast/AST_accept.h12
-rw-r--r--libsolidity/ast/Types.cpp302
-rw-r--r--libsolidity/ast/Types.h51
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp3
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp53
-rw-r--r--libsolidity/codegen/ArrayUtils.h6
-rw-r--r--libsolidity/codegen/CompilerContext.cpp32
-rw-r--r--libsolidity/codegen/CompilerContext.h9
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp284
-rw-r--r--libsolidity/codegen/CompilerUtils.h28
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp158
-rw-r--r--libsolidity/codegen/ContractCompiler.h6
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp275
-rw-r--r--libsolidity/formal/SMTChecker.cpp49
-rw-r--r--libsolidity/formal/SSAVariable.cpp21
-rw-r--r--libsolidity/formal/SSAVariable.h8
-rw-r--r--libsolidity/formal/SolverInterface.h32
-rw-r--r--libsolidity/formal/SymbolicBoolVariable.cpp47
-rw-r--r--libsolidity/formal/SymbolicBoolVariable.h50
-rw-r--r--libsolidity/formal/SymbolicIntVariable.cpp12
-rw-r--r--libsolidity/formal/SymbolicIntVariable.h5
-rw-r--r--libsolidity/formal/SymbolicVariable.cpp6
-rw-r--r--libsolidity/formal/SymbolicVariable.h12
-rw-r--r--libsolidity/interface/CompilerStack.cpp143
-rw-r--r--libsolidity/interface/EVMVersion.h2
-rw-r--r--libsolidity/interface/ErrorReporter.cpp37
-rw-r--r--libsolidity/interface/ErrorReporter.h9
-rw-r--r--libsolidity/interface/GasEstimator.cpp9
-rw-r--r--libsolidity/parsing/DocStringParser.cpp10
-rw-r--r--libsolidity/parsing/Parser.cpp71
-rw-r--r--libsolidity/parsing/Parser.h7
-rw-r--r--libsolidity/parsing/Token.cpp2
-rwxr-xr-xscripts/cpp-ethereum/build.sh17
-rw-r--r--scripts/cpp-ethereum/eth_artful.docker7
-rw-r--r--scripts/cpp-ethereum/eth_trusty.docker13
-rwxr-xr-xscripts/extract_test_cases.py49
-rwxr-xr-xscripts/isolate_tests.py7
-rwxr-xr-xscripts/isoltest.sh6
-rwxr-xr-xscripts/soltest.sh43
-rwxr-xr-xscripts/tests.sh25
-rwxr-xr-xscripts/uniqueErrors.sh2
-rw-r--r--solc/CommandLineInterface.cpp51
-rw-r--r--solc/CommandLineInterface.h2
-rw-r--r--std/StandardToken.sol4
-rw-r--r--std/owned.sol4
-rw-r--r--test/CMakeLists.txt30
-rw-r--r--test/ExecutionFramework.h2
-rw-r--r--test/Options.cpp (renamed from test/TestHelper.cpp)25
-rw-r--r--test/Options.h (renamed from test/TestHelper.h)2
-rw-r--r--test/RPCSession.cpp8
-rw-r--r--test/boostTest.cpp9
-rwxr-xr-xtest/cmdlineTests.sh6
-rw-r--r--test/libdevcore/Checksum.cpp2
-rw-r--r--test/libdevcore/IndentedWriter.cpp2
-rw-r--r--test/libdevcore/JSON.cpp2
-rw-r--r--test/libdevcore/StringUtils.cpp2
-rw-r--r--test/libdevcore/SwarmHash.cpp2
-rw-r--r--test/libdevcore/UTF8.cpp2
-rw-r--r--test/libdevcore/Whiskers.cpp2
-rw-r--r--test/libevmasm/Optimiser.cpp116
-rw-r--r--test/libevmasm/SourceLocation.cpp2
-rw-r--r--test/libjulia/Common.cpp2
-rw-r--r--test/libjulia/CommonSubexpression.cpp102
-rw-r--r--test/libjulia/Parser.cpp2
-rw-r--r--test/liblll/Compiler.cpp2
-rw-r--r--test/liblll/EndToEndTest.cpp2
-rw-r--r--test/libsolidity/ABIDecoderTests.cpp83
-rw-r--r--test/libsolidity/ASTJSON.cpp228
-rw-r--r--test/libsolidity/ASTLegacyJSON.cpp324
-rw-r--r--test/libsolidity/AnalysisFramework.cpp30
-rw-r--r--test/libsolidity/AnalysisFramework.h7
-rw-r--r--test/libsolidity/Assembly.cpp23
-rw-r--r--test/libsolidity/FormattedScope.h68
-rw-r--r--test/libsolidity/GasMeter.cpp13
-rw-r--r--test/libsolidity/Imports.cpp2
-rw-r--r--test/libsolidity/InlineAssembly.cpp4
-rw-r--r--test/libsolidity/JSONCompiler.cpp16
-rw-r--r--test/libsolidity/Metadata.cpp4
-rw-r--r--test/libsolidity/SMTChecker.cpp138
-rw-r--r--test/libsolidity/SemVerMatcher.cpp2
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp2
-rw-r--r--test/libsolidity/SolidityCompiler.cpp60
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp1077
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp11
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp1271
-rw-r--r--test/libsolidity/SolidityNatspecJSON.cpp2
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp64
-rw-r--r--test/libsolidity/SolidityParser.cpp46
-rw-r--r--test/libsolidity/SolidityTypes.cpp1
-rw-r--r--test/libsolidity/StandardCompiler.cpp21
-rw-r--r--test/libsolidity/SyntaxTest.cpp283
-rw-r--r--test/libsolidity/SyntaxTest.h91
-rw-r--r--test/libsolidity/ViewPureChecker.cpp33
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol7
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol8
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol7
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol6
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol6
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol7
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol10
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol6
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/constant_var.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol8
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/inline_array.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol6
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/parentheses.sol25
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/pure_functions.sol6
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/too_large.sol5
-rw-r--r--test/libsolidity/syntaxTests/arrayLength/tuples.sol5
-rw-r--r--test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol7
-rw-r--r--test/libsolidity/syntaxTests/constants/addmod_zero.sol11
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol5
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol10
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol11
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol6
-rw-r--r--test/libsolidity/syntaxTests/constants/division_by_zero.sol9
-rw-r--r--test/libsolidity/syntaxTests/constants/mod_div_rational.sol6
-rw-r--r--test/libsolidity/syntaxTests/constants/mod_zero.sol9
-rw-r--r--test/libsolidity/syntaxTests/constants/mulmod_zero.sol11
-rw-r--r--test/libsolidity/syntaxTests/constants/pure_non_rational.sol11
-rw-r--r--test/libsolidity/syntaxTests/constructor_this.sol12
-rw-r--r--test/libsolidity/syntaxTests/docstring_empty_description.sol6
-rw-r--r--test/libsolidity/syntaxTests/double_stateVariable_declaration.sol6
-rw-r--r--test/libsolidity/syntaxTests/double_variable_declaration.sol8
-rw-r--r--test/libsolidity/syntaxTests/double_variable_declaration_050.sol11
-rw-r--r--test/libsolidity/syntaxTests/empty_struct.sol5
-rw-r--r--test/libsolidity/syntaxTests/empty_struct_050.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol13
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol8
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol17
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol10
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol11
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol11
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol8
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol9
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol8
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol4
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol9
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/private_function_type.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/public_function_type.sol7
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol23
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol5
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol5
-rw-r--r--test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol2
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol7
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol9
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol9
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol5
-rw-r--r--test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol4
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol5
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol7
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol4
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol6
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol7
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol6
-rw-r--r--test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol6
-rw-r--r--test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol10
-rw-r--r--test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol5
-rw-r--r--test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol5
-rw-r--r--test/libsolidity/syntaxTests/literalOperations/mod_zero.sol5
-rw-r--r--test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol5
-rw-r--r--test/libsolidity/syntaxTests/literal_comparisons.sol7
-rw-r--r--test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol7
-rw-r--r--test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol9
-rw-r--r--test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol4
-rw-r--r--test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol5
-rw-r--r--test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol4
-rw-r--r--test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol7
-rw-r--r--test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol5
-rw-r--r--test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol5
-rw-r--r--test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol4
-rw-r--r--test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol6
-rw-r--r--test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol2
-rw-r--r--test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol5
-rw-r--r--test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol6
-rw-r--r--test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol5
-rw-r--r--test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol10
-rw-r--r--test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol13
-rw-r--r--test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol524
-rw-r--r--test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol524
-rw-r--r--test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol28
-rw-r--r--test/libsolidity/syntaxTests/parsing/constructor_super.sol10
-rw-r--r--test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/return_var.sol25
-rw-r--r--test/libsolidity/syntaxTests/parsing/smoke_test.sol4
-rw-r--r--test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol25
-rw-r--r--test/libsolidity/syntaxTests/parsing/var_storage_var.sol5
-rw-r--r--test/libsolidity/syntaxTests/scoping/double_function_declaration.sol6
-rw-r--r--test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol8
-rw-r--r--test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol10
-rw-r--r--test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol8
-rw-r--r--test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol10
-rw-r--r--test/libsolidity/syntaxTests/scoping/name_shadowing.sol6
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping.sol11
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_activation.sol9
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol6
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_for.sol8
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_for2.sol7
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_for3.sol11
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol10
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_old.sol6
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_self_use.sol5
-rw-r--r--test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol8
-rw-r--r--test/libsolidity/syntaxTests/signed_rational_modulus.sol8
-rw-r--r--test/libsolidity/syntaxTests/smoke_test.sol6
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol17
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol18
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol11
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol13
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol13
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol16
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol17
-rw-r--r--test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol9
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol15
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol15
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol7
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol7
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol8
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol8
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol12
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol4
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol7
-rw-r--r--test/libsolidity/syntaxTests/tight_packing_literals.sol25
-rw-r--r--test/libsolidity/syntaxTests/tight_packing_literals_fine.sol11
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol5
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol13
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_div_limit.sol9
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol50
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol9
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol9
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol9
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol9
-rw-r--r--test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol8
-rw-r--r--test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol36
-rw-r--r--test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol14
-rw-r--r--test/libsolidity/syntaxTests/visibility/external_library_function.sol14
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_default.sol6
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_default050.sol7
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_external050.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_internal.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_private.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_public.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_public050.sol6
-rw-r--r--test/tools/CMakeLists.txt5
-rw-r--r--test/tools/fuzzer.cpp (renamed from test/fuzzer.cpp)0
-rw-r--r--test/tools/isoltest.cpp396
328 files changed, 8687 insertions, 2245 deletions
diff --git a/.travis.yml b/.travis.yml
index 0b05f661..539e6088 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -84,12 +84,12 @@ matrix:
sudo: required
compiler: gcc
node_js:
- - "7"
+ - "8"
services:
- docker
before_install:
- - nvm install 7
- - nvm use 7
+ - nvm install 8
+ - nvm use 8
- docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit
env:
- SOLC_EMSCRIPTEN=On
@@ -159,6 +159,8 @@ cache:
install:
- test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh)
- test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh)
+# - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" ]; then SOLC_TESTS=Off; fi
+ - SOLC_TESTS=Off
- if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
- echo -n "$TRAVIS_COMMIT" > commit_hash.txt
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d4ff161..208e405d 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.21")
+set(PROJECT_VERSION "0.4.22")
project(solidity VERSION ${PROJECT_VERSION})
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
diff --git a/CODING_STYLE.md b/CODING_STYLE.md
new file mode 100644
index 00000000..2cc9ac70
--- /dev/null
+++ b/CODING_STYLE.md
@@ -0,0 +1,262 @@
+0. Formatting
+
+GOLDEN RULE: Follow the style of the existing code when you make changes.
+
+a. Use tabs for leading indentation
+- tab stops are every 4 characters (only relevant for line length).
+- One indentation level -> exactly one byte (i.e. a tab character) in the source file.
+b. Line widths:
+- Lines should be at most 99 characters wide to make diff views readable and reduce merge conflicts.
+- Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty.
+c. Single-statement blocks should not have braces, unless required for clarity.
+d. Never place condition bodies on same line as condition.
+e. Space between keyword and opening parenthesis, but not following opening parenthesis or before final parenthesis.
+f. No spaces for unary operators, `->` or `.`.
+g. No space before ':' but one after it, except in the ternary operator: one on both sides.
+h. Add spaces around all other operators.
+i. Braces, when used, always have their own lines and are at same indentation level as "parent" scope.
+j. If lines are broken, a list of elements enclosed with parentheses (of any kind) and separated by a
+ separator (of any kind) are formatted such that there is exactly one element per line, followed by
+ the separator, the opening parenthesis is on the first line, followed by a line break and the closing
+ parenthesis is on a line of its own (unindented). See example below.
+
+(WRONG)
+if( a==b[ i ] ) { printf ("Hello\n"); }
+foo->bar(someLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName);
+cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl;
+
+(RIGHT)
+if (a == b[i])
+ printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'.
+foo->bar(
+ someLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName
+);
+cout <<
+ "some very long string that contains completely irrelevant " <<
+ "text that talks about this and that and contains the words " <<
+ "\"lorem\" and \"ipsum\"" <<
+ endl;
+
+
+
+1. Namespaces;
+
+a. No "using namespace" declarations in header files.
+b. All symbols should be declared in a namespace except for final applications.
+c. Use anonymous namespaces for helpers whose scope is a cpp file only.
+d. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore.
+
+(WRONG)
+#include <cassert>
+using namespace std;
+tuple<float, float> meanAndSigma(vector<float> const& _v);
+
+(CORRECT)
+#include <cassert>
+std::tuple<float, float> meanAndSigma(std::vector<float> const& _v);
+
+
+
+2. Preprocessor;
+
+a. File comment is always at top, and includes:
+- Copyright.
+- License (e.g. see COPYING).
+b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment.
+c. Prefer static const variable to value macros.
+d. Prefer inline constexpr functions to function macros.
+e. Split complex macro on multiple lines with '\'.
+
+
+
+3. Capitalization;
+
+GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase.
+
+a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions.
+b. The following entities' first alpha is upper case:
+- Type names.
+- Template parameters.
+- Enum members.
+- static const variables that form an external API.
+c. All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation.
+
+
+All other entities' first alpha is lower case.
+
+
+
+4. Variable prefixes:
+
+a. Leading underscore "_" to parameter names.
+- Exception: "o_parameterName" when it is used exclusively for output. See 6(f).
+- Exception: "io_parameterName" when it is used for both input and output. See 6(f).
+b. Leading "g_" to global (non-const) variables.
+c. Leading "s_" to static (non-const, non-global) variables.
+
+
+
+5. Assertions:
+
+- use `solAssert` and `solUnimplementedAssert` generously to check assumptions
+ that span across different parts of the code base, for example before dereferencing
+ a pointer.
+
+
+6. Declarations:
+
+a. {Typename} + {qualifiers} + {name}.
+b. Only one per line.
+c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)).
+d. Favour declarations close to use; don't habitually declare at top of scope ala C.
+e. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move.
+f. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments.
+g. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer.
+h. Never use a macro where adequate non-preprocessor C++ can be written.
+i. Only use ``auto`` if the type is very long and rather irrelevant.
+j. Do not pass bools: prefer enumerations instead.
+k. Prefer enum class to straight enum.
+l. Always initialize POD variables, even if their value is overwritten later.
+
+
+(WRONG)
+const double d = 0;
+int i, j;
+char *s;
+float meanAndSigma(std::vector<float> _v, float* _sigma, bool _approximate);
+Derived* x(dynamic_cast<Derived*>(base));
+for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); ++l) {}
+
+
+(CORRECT)
+enum class Accuracy
+{
+ Approximate,
+ Exact
+};
+struct MeanSigma
+{
+ float mean;
+ float standardDeviation;
+};
+double const d = 0;
+int i;
+int j;
+char* s;
+MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
+Derived* x = dynamic_cast<Derived*>(base);
+for (auto i = x->begin(); i != x->end(); ++i) {}
+
+
+7. Structs & classes
+
+a. Structs to be used when all members public and no virtual functions.
+- In this case, members should be named naturally and not prefixed with 'm_'
+b. Classes to be used in all other circumstances.
+
+
+
+8. Members:
+
+a. One member per line only.
+b. Private, non-static, non-const fields prefixed with m_.
+c. Avoid public fields, except in structs.
+d. Use override, final and const as much as possible.
+e. No implementations with the class declaration, except:
+- template or force-inline method (though prefer implementation at bottom of header file).
+- one-line implementation (in which case include it in same line as declaration).
+f. For a property 'foo'
+- Member: m_foo;
+- Getter: foo() [ also: for booleans, isFoo() ];
+- Setter: setFoo();
+
+
+
+9. Naming
+
+a. Avoid unpronouncable names
+b. Names should be shortened only if they are extremely common, but shortening should be generally avoided
+c. Avoid prefixes of initials (e.g. do not use IMyInterface, CMyImplementation)
+c. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments.
+- A dictionary and thesaurus are your friends.
+- Spell correctly.
+- Think carefully about the class's purpose.
+- Imagine it as an isolated component to try to decontextualise it when considering its name.
+- Don't be trapped into naming it (purely) in terms of its implementation.
+
+
+
+10. Type-definitions
+
+a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector<int>; rather than typedef std::vector<int> ints;
+b. Generally avoid shortening a standard form that already includes all important information:
+- e.g. stick to shared_ptr<X> rather than shortening to ptr<X>.
+c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently.
+- e.g. using Guard = std::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it is clear in meaning and used commonly.
+d. In general expressions should be roughly as important/semantically meaningful as the space they occupy.
+e. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time.
+
+
+
+11. Commenting
+
+a. Comments should be doxygen-compilable, using @notation rather than \notation.
+b. Document the interface, not the implementation.
+- Documentation should be able to remain completely unchanged, even if the method is reimplemented.
+- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports).
+- Be careful to scrutinise documentation that extends only to intended purpose and usage.
+- Reject documentation that is simply an English transaction of the implementation.
+c. Avoid in-code comments. Instead, try to extract blocks of functionality into functions. This often already eliminates the need for an in-code comment.
+
+
+12. Include Headers
+
+Includes should go in increasing order of generality (libsolidity -> libevmasm -> libdevcore -> boost -> STL).
+The corresponding .h file should be the first include in the respective .cpp file.
+Insert empty lines between blocks of include files.
+
+Example:
+
+```
+#include <libsolidity/codegen/ExpressionCompiler.h>
+
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+#include <libsolidity/codegen/LValue.h>
+
+#include <libevmasm/GasMeter.h>
+
+#include <libdevcore/Common.h>
+#include <libdevcore/SHA3.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+#include <utility>
+#include <numeric>
+```
+
+See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files.
+
+
+13. Recommended reading
+
+Herb Sutter and Bjarne Stroustrup
+- "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md)
+
+Herb Sutter and Andrei Alexandrescu
+- "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices"
+
+Scott Meyers
+- "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)"
+- "More Effective C++: 35 New Ways to Improve Your Programs and Designs"
+- "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14"
diff --git a/Changelog.md b/Changelog.md
index 98528893..0015c949 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,56 @@
+### 0.4.22 (2018-04-16)
+
+Features:
+ * Code Generator: Initialize arrays without using ``msize()``.
+ * Code Generator: More specialized and thus optimized implementation for ``x.push(...)``
+ * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag.
+ * Constant Evaluator: Fix evaluation of single element tuples.
+ * General: Add encoding routines ``abi.encodePacked``, ``abi.encode``, ``abi.encodeWithSelector`` and ``abi.encodeWithSignature``.
+ * General: Add global function ``gasleft()`` and deprecate ``msg.gas``.
+ * General: Add global function ``blockhash(uint)`` and deprecate ``block.hash(uint)``.
+ * General: Allow providing reason string for ``revert()`` and ``require()``.
+ * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature.
+ * General: Limit the number of errors output in a single run to 256.
+ * General: Support accessing dynamic return data in post-byzantium EVMs.
+ * Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature.
+ * Inheritance: Error when using no parentheses in modifier-style constructor calls as experimental 0.5.0 feature.
+ * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract.
+ * Optimizer: Optimize ``SHL`` and ``SHR`` only involving constants (Constantinople only).
+ * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc).
+ * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``.
+ * Optimizer: Optimize across ``mload`` if ``msize()`` is not used.
+ * Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature.
+ * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature).
+ * Syntax Checker: Warn about modifiers on functions without implementation (this will turn into an error with version 0.5.0).
+ * Syntax Tests: Add source locations to syntax test expectations.
+ * Type Checker: Improve documentation and warnings for accessing contract members inherited from ``address``.
+
+
+Bugfixes:
+ * Code Generator: Allow ``block.blockhash`` without being called.
+ * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor.
+ * Code Generator: Properly skip unneeded storage array cleanup when not reducing length.
+ * Code Generator: Bugfix in modifier lookup in libraries.
+ * Code Generator: Implement packed encoding of external function types.
+ * Code Generator: Treat empty base constructor argument list as not provided.
+ * Code Generator: Properly force-clean bytesXX types for shortening conversions.
+ * Commandline interface: Fix error messages for imported files that do not exist.
+ * Commandline interface: Support ``--evm-version constantinople`` properly.
+ * DocString Parser: Fix error message for empty descriptions.
+ * Gas Estimator: Correctly ignore costs of fallback function for other functions.
+ * JSON AST: Remove storage qualifier for type name strings.
+ * Parser: Fix internal compiler error when parsing ``var`` declaration without identifier.
+ * Parser: Fix parsing of getters for function type variables.
+ * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly.
+ * Static Analyzer: Fix non-deterministic order of unused variable warnings.
+ * Static Analyzer: Invalid arithmetic with constant expressions causes errors.
+ * Type Checker: Fix detection of recursive structs.
+ * Type Checker: Fix asymmetry bug when comparing with literal numbers.
+ * Type System: Improve error message when attempting to shift by a fractional amount.
+ * Type System: Make external library functions accessible.
+ * Type System: Prevent encoding of weird types.
+ * Type System: Restrict rational numbers to 4096 bits.
+
### 0.4.21 (2018-03-07)
Features:
diff --git a/README.md b/README.md
index cb743729..42c392e2 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
## Useful links
To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](https://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](https://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts.
-You can start using [Solidity in your browser](https://ethereum.github.io/browser-solidity/) with no need to download or compile anything.
+You can start using [Solidity in your browser](http://remix.ethereum.org) with no need to download or compile anything.
The changelog for this project can be found [here](https://github.com/ethereum/solidity/blob/develop/Changelog.md).
diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md
index b5df9fda..82b1308c 100644
--- a/ReleaseChecklist.md
+++ b/ReleaseChecklist.md
@@ -11,6 +11,7 @@ Checklist for making a release:
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems).
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
+ - [ ] Update the default version on readthedocs.
- [ ] Make a release of ``solc-js``: Increment the version number, create a pull request for that, merge it after tests succeeded.
- [ ] Run ``npm publish`` in the updated ``solc-js`` repository.
- [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry.
diff --git a/appveyor.yml b/appveyor.yml
index ef5f6907..5fd85482 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -71,7 +71,7 @@ build_script:
test_script:
- cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION%
- - soltest.exe --show-progress -- --no-ipc --no-smt
+ - soltest.exe --show-progress -- --testpath %APPVEYOR_BUILD_FOLDER%\test --no-ipc --no-smt
# Skip bytecode compare if private key is not available
- cd %APPVEYOR_BUILD_FOLDER%
- ps: if ($env:priv_key) {
diff --git a/circle.yml b/circle.yml
index 263cb700..f97b619a 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,3 +1,10 @@
+defaults:
+ # The default for tags is to not run, so we have to explicitly match a filter.
+ - build_on_tags: &build_on_tags
+ filters:
+ tags:
+ only: /.*/
+
version: 2
jobs:
build_emscripten:
@@ -48,7 +55,7 @@ jobs:
export NVM_DIR="/usr/local/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
nvm --version
- nvm install 6
+ nvm install 8
node --version
npm --version
- run:
@@ -72,7 +79,7 @@ jobs:
export NVM_DIR="/usr/local/nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
nvm --version
- nvm install 7
+ nvm install 8
node --version
npm --version
- run:
@@ -89,20 +96,12 @@ jobs:
name: Install build dependencies
command: |
apt-get -qq update
- apt-get -qy install ccache cmake libboost-all-dev libz3-dev
+ apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev
- 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
- - restore_cache:
- key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- key: ccache-{{ arch }}-{{ .Branch }}-
- key: ccache-{{ arch }}-develop-
- key: ccache-{{ arch }}-
- - run:
- name: Configure ccache
- command: ccache -M 200M && ccache -c && ccache -s && ccache -z
- run:
name: Build
command: |
@@ -110,14 +109,6 @@ jobs:
cd build
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
make -j4
- - run:
- name: CCache statistics
- command: ccache -s
- - save_cache:
- key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
- when: always
- paths:
- - ~/.ccache
- store_artifacts:
path: build/solc/solc
destination: solc
@@ -126,7 +117,7 @@ jobs:
paths:
- solc/solc
- test/soltest
- - test/solfuzzer
+ - test/tools/solfuzzer
test_x86:
docker:
@@ -173,15 +164,18 @@ workflows:
version: 2
build_all:
jobs:
- - build_emscripten
+ - build_emscripten: *build_on_tags
- test_emscripten_solcjs:
+ <<: *build_on_tags
requires:
- build_emscripten
- test_emscripten_external:
+ <<: *build_on_tags
requires:
- build_emscripten
- - build_x86
+ - build_x86: *build_on_tags
- test_x86:
+ <<: *build_on_tags
requires:
- build_x86
- - docs
+ - docs: *build_on_tags
diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake
index 233ac22a..cc2f8711 100644
--- a/cmake/EthDependencies.cmake
+++ b/cmake/EthDependencies.cmake
@@ -34,13 +34,6 @@ endif()
set(ETH_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR})
set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts)
-find_program(CTEST_COMMAND ctest)
-
-#message(STATUS "CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}")
-#message(STATUS "CMake Helper Path: ${ETH_CMAKE_DIR}")
-#message(STATUS "CMake Script Path: ${ETH_SCRIPTS_DIR}")
-#message(STATUS "ctest path: ${CTEST_COMMAND}")
-
## use multithreaded boost libraries, with -mt suffix
set(Boost_USE_MULTITHREADED ON)
option(Boost_USE_STATIC_LIBS "Link Boost statically" ON)
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 4a61d91f..98301fdc 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -54,11 +54,11 @@ The following elementary types exist:
- ``ufixed<M>x<N>``: unsigned variant of ``fixed<M>x<N>``.
-- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively. For computing the function selector, ``fixed128x19`` and ``ufixed128x19`` have to be used.
+- ``fixed``, ``ufixed``: synonyms for ``fixed128x18``, ``ufixed128x18`` respectively. For computing the function selector, ``fixed128x18`` and ``ufixed128x18`` have to be used.
- ``bytes<M>``: binary type of ``M`` bytes, ``0 < M <= 32``.
-- ``function``: an address (20 bytes) folled by a function selector (4 bytes). Encoded identical to ``bytes24``.
+- ``function``: an address (20 bytes) followed by a function selector (4 bytes). Encoded identical to ``bytes24``.
The following (fixed-size) array type exists:
@@ -164,9 +164,9 @@ on the type of ``X`` being
- ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes.
- ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false``
- ``fixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``.
-- ``fixed``: as in the ``fixed128x19`` case
+- ``fixed``: as in the ``fixed128x18`` case
- ``ufixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``.
-- ``ufixed``: as in the ``ufixed128x19`` case
+- ``ufixed``: as in the ``ufixed128x18`` case
- ``bytes<M>``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes.
Note that for any ``X``, ``len(enc(X))`` is a multiple of 32.
diff --git a/docs/assembly.rst b/docs/assembly.rst
index cf9bf840..978e71e3 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -405,6 +405,16 @@ changes during the call, and thus references to local variables will be wrong.
}
}
+.. note::
+ If you access variables of a type that spans less than 256 bits
+ (for example ``uint64``, ``address``, ``bytes16`` or ``byte``),
+ you cannot make any assumptions about bits not part of the
+ encoding of the type. Especially, do not assume them to be zero.
+ To be safe, always clear the data properly before you use it
+ in a context where this is important:
+ ``uint32 x = f(); assembly { x := and(x, 0xffffffff) /* now use x */ }``
+ To clean signed types, you can use the ``signextend`` opcode.
+
Labels
------
@@ -647,6 +657,11 @@ Solidity manages memory in a very simple way: There is a "free memory pointer"
at position ``0x40`` in memory. If you want to allocate memory, just use the memory
from that point on and update the pointer accordingly.
+The first 64 bytes of memory can be used as "scratch space" for short-term
+allocation. The 32 bytes after the free memory pointer (i.e. starting at ``0x60``)
+is meant to be zero permanently and is used as the initial value for
+empty dynamic memory arrays.
+
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is
even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index 4c976a32..32f305c8 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -422,6 +422,10 @@
"bugs": [],
"released": "2018-03-07"
},
+ "0.4.22": {
+ "bugs": [],
+ "released": "2018-04-16"
+ },
"0.4.3": {
"bugs": [
"ZeroFunctionSelector",
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst
index 7e09f534..739e136f 100644
--- a/docs/common-patterns.rst
+++ b/docs/common-patterns.rst
@@ -130,7 +130,7 @@ restrictions highly readable.
::
- pragma solidity ^0.4.11;
+ pragma solidity ^0.4.22;
contract AccessRestriction {
// These will be assigned at the construction
@@ -147,7 +147,10 @@ restrictions highly readable.
// a certain address.
modifier onlyBy(address _account)
{
- require(msg.sender == _account);
+ require(
+ msg.sender == _account,
+ "Sender not authorized."
+ );
// Do not forget the "_;"! It will
// be replaced by the actual function
// body when the modifier is used.
@@ -164,7 +167,10 @@ restrictions highly readable.
}
modifier onlyAfter(uint _time) {
- require(now >= _time);
+ require(
+ now >= _time,
+ "Function called too early."
+ );
_;
}
@@ -186,7 +192,10 @@ restrictions highly readable.
// This was dangerous before Solidity version 0.4.0,
// where it was possible to skip the part after `_;`.
modifier costs(uint _amount) {
- require(msg.value >= _amount);
+ require(
+ msg.value >= _amount,
+ "Not enough Ether provided."
+ );
_;
if (msg.value > _amount)
msg.sender.send(msg.value - _amount);
@@ -194,6 +203,7 @@ restrictions highly readable.
function forceOwnerChange(address _newOwner)
public
+ payable
costs(200 ether)
{
owner = _newOwner;
@@ -272,7 +282,7 @@ function finishes.
::
- pragma solidity ^0.4.11;
+ pragma solidity ^0.4.22;
contract StateMachine {
enum Stages {
@@ -289,7 +299,10 @@ function finishes.
uint public creationTime = now;
modifier atStage(Stages _stage) {
- require(stage == _stage);
+ require(
+ stage == _stage,
+ "Function cannot be called at this time."
+ );
_;
}
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 121c4de0..a1f2895c 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible.
::
- pragma solidity ^0.4.16;
+ pragma solidity ^0.4.22;
contract OwnedToken {
// TokenCreator is a contract type that is defined below.
@@ -52,7 +52,7 @@ This means that cyclic creation dependencies are impossible.
// This is the constructor which registers the
// creator and the assigned name.
- function OwnedToken(bytes32 _name) public {
+ constructor(bytes32 _name) public {
// State variables are accessed via their name
// and not via e.g. this.owner. This also applies
// to functions and especially in the constructors,
@@ -301,7 +301,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
::
- pragma solidity ^0.4.11;
+ pragma solidity ^0.4.22;
contract owned {
function owned() public { owner = msg.sender; }
@@ -315,7 +315,10 @@ inheritable properties of contracts and may be overridden by derived contracts.
// function is executed and otherwise, an exception is
// thrown.
modifier onlyOwner {
- require(msg.sender == owner);
+ require(
+ msg.sender == owner,
+ "Only owner can call this function."
+ );
_;
}
}
@@ -360,7 +363,10 @@ inheritable properties of contracts and may be overridden by derived contracts.
contract Mutex {
bool locked;
modifier noReentrancy() {
- require(!locked);
+ require(
+ !locked,
+ "Reentrant call."
+ );
locked = true;
_;
locked = false;
@@ -679,7 +685,7 @@ candidate, resolution fails.
}
}
-Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8``
+Calling ``f(50)`` would create a type error since ``50`` can be implicitly converted both to ``uint8``
and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly
converted to ``uint8``.
@@ -803,7 +809,7 @@ as topics. The event call above can be performed in the same way as
}
where the long hexadecimal number is equal to
-``keccak256("Deposit(address,hash256,uint256)")``, the signature of the event.
+``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event.
Additional Resources for Understanding Events
==============================================
@@ -976,8 +982,31 @@ virtual method lookup.
Constructors
============
-A constructor is an optional function with the same name as the contract which is executed upon contract creation.
-Constructor functions can be either ``public`` or ``internal``.
+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 {}``.
+
+
+::
+
+ pragma solidity ^0.4.22;
+
+ contract A {
+ uint public a;
+
+ constructor(uint _a) internal {
+ a = _a;
+ }
+ }
+
+ contract B is A(1) {
+ constructor() public {}
+ }
+
+A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
+
+.. note ::
+ Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax is now deprecated.
::
@@ -995,7 +1024,6 @@ Constructor functions can be either ``public`` or ``internal``.
function B() public {}
}
-A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
.. index:: ! base;constructor
@@ -1009,12 +1037,15 @@ the base constructors. This can be done in two ways::
contract Base {
uint x;
- function Base(uint _x) public { x = _x; }
+ constructor(uint _x) public { x = _x; }
}
- contract Derived is Base(7) {
- function Derived(uint _y) Base(_y * _y) public {
- }
+ contract Derived1 is Base(7) {
+ constructor(uint _y) public {}
+ }
+
+ contract Derived2 is Base {
+ constructor(uint _y) Base(_y * _y) public {}
}
One way is directly in the inheritance list (``is Base(7)``). The other is in
@@ -1024,8 +1055,9 @@ do it is more convenient if the constructor argument is a
constant and defines the behaviour of the contract or
describes it. The second way has to be used if the
constructor arguments of the base depend on those of the
-derived contract. If, as in this silly example, both places
-are used, the modifier-style argument takes precedence.
+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.
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
@@ -1183,7 +1215,7 @@ more advanced example to implement a set).
::
- pragma solidity ^0.4.16;
+ pragma solidity ^0.4.22;
library Set {
// We define a new struct datatype that will be used to
diff --git a/docs/contributing.rst b/docs/contributing.rst
index a5efba8b..6717a8b9 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -55,8 +55,8 @@ However, if you are making a larger change, please consult with the `Solidity De
focused on compiler and language development instead of language use) first.
-Finally, please make sure you respect the `coding standards
-<https://raw.githubusercontent.com/ethereum/cpp-ethereum/develop/CodingStandards.txt>`_
+Finally, please make sure you respect the `coding style
+<https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_
for this project. Also, even though we do CI testing, please test your code and
ensure that it builds locally before submitting a pull request.
@@ -69,21 +69,158 @@ Solidity includes different types of tests. They are included in the application
called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode,
some others require ``libz3`` to be installed.
-To disable the z3 tests, use ``./build/test/soltest -- --no-smt`` and
-to run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``.
+``soltest`` reads test contracts that are annotated with expected results
+stored in ``./test/libsolidity/syntaxTests``. In order for soltest to find these
+tests the root test directory has to be specified using the ``--testpath`` command
+line option, e.g. ``./build/test/soltest -- --testpath ./test``.
+
+To disable the z3 tests, use ``./build/test/soltest -- --no-smt --testpath ./test`` and
+to run a subset of the tests that do not require ``cpp-ethereum``, use
+``./build/test/soltest -- --no-ipc --testpath ./test``.
For all other tests, you need to install `cpp-ethereum <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth>`_ and run it in testing mode: ``eth --test -d /tmp/testeth``.
-Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc``.
+Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``.
To run a subset of tests, filters can be used:
-``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``.
+``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``,
+where ``TestName`` can be a wildcard ``*``.
Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests and runs
``cpp-ethereum`` automatically if it is in the path (but does not download it).
Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target.
+Writing and running syntax tests
+--------------------------------
+
+As mentioned above, syntax tests are stored in individual contracts. These files must contain annotations, stating the expected result(s) of the respective test.
+The test suite will compile and check them against the given expectations.
+
+Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol``
+
+::
+
+ contract test {
+ uint256 variable;
+ uint128 variable;
+ }
+ // ----
+ // DeclarationError: Identifier already declared.
+
+A syntax test must contain at least the contract under test itself, followed by the seperator ``----``. The additional comments above are used to describe the
+expected compiler errors or warnings. This section can be empty in case that the contract should compile without any errors or warnings.
+
+In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifer was already declared.
+
+The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows
+editing of failing contracts using your prefered text editor. Let's try to break this test by removing the second declaration of ``variable``:
+
+::
+
+ contract test {
+ uint256 variable;
+ }
+ // ----
+ // DeclarationError: Identifier already declared.
+
+Running ``./test/isoltest`` again will result in a test failure:
+
+::
+
+ syntaxTests/double_stateVariable_declaration.sol: FAIL
+ Contract:
+ contract test {
+ uint256 variable;
+ }
+
+ Expected result:
+ DeclarationError: Identifier already declared.
+ Obtained result:
+ Success
+
+
+which prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit.
+``isoltest`` offers several options for failing tests:
+
+- edit: ``isoltest`` will try to open the editor that was specified before using ``isoltest --editor /path/to/editor``. If no path was set, this will result in a runtime error. In case an editor was specified, this will open it such that the contract can be adjusted.
+- update: Updates the contract under test. This will either remove the annotation which contains the exception not met or will add missing expectations. The test will then be run again.
+- skip: Skips the execution of this particular test.
+- quit: Quits ``isoltest``.
+
+Automatically updating the test above will change it to
+
+::
+
+ contract test {
+ uint256 variable;
+ }
+ // ----
+
+and re-run the test. It will now pass again:
+
+::
+
+ Re-running test case...
+ syntaxTests/double_stateVariable_declaration.sol: OK
+
+
+.. note::
+
+ Please choose a name for the contract file, that is self-explainatory in the sense of what is been tested, e.g. ``double_variable_declaration.sol``.
+ Do not put more than one contract into a single file. ``isoltest`` is currently not able to recognize them individually.
+
+
+Running the Fuzzer via AFL
+==========================
+
+Fuzzing is a technique that runs programs on more or less random inputs to find exceptional execution
+states (segmentation faults, exceptions, etc). Modern fuzzers are clever and do a directed search
+inside the input. We have a specialized binary called ``solfuzzer`` which takes source code as input
+and fails whenever it encounters an internal compiler error, segmentation fault or similar, but
+does not fail if e.g. the code contains an error. This way, internal problems in the compiler
+can be found by fuzzing tools.
+
+We mainly use `AFL <http://lcamtuf.coredump.cx/afl/>`_ for fuzzing. You need to download and
+build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler:
+
+::
+
+ cd build
+ # if needed
+ make clean
+ cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++
+ make solfuzzer
+
+Next, you need some example source files. This will make it much easer for the fuzzer
+to find errors. You can either copy some files from the syntax tests or extract test files
+from the documentation or the other tests:
+
+::
+
+ mkdir /tmp/test_cases
+ cd /tmp/test_cases
+ # extract from tests:
+ path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp
+ # extract from documentation:
+ path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs
+
+The AFL documentation states that the corpus (the initial input files) should not be
+too large. The files themselves should not be larger than 1 kB and there should be
+at most one input file per functionality, so better start with a small number of
+input files. There is also a tool called ``afl-cmin`` that can trim input files
+that result in similar behaviour of the binary.
+
+Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB):
+
+::
+
+ afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer
+
+The fuzzer will create source files that lead to failures in ``/tmp/fuzzer_reports``.
+Often it finds many similar source files that produce the same error. You can
+use the tool ``scripts/uniqueErrors.sh`` to filter out the unique errors.
+
Whiskers
========
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index 46e076e5..f3c351dd 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -284,10 +284,12 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di
}
function g() public {
- // Declares and assigns the variables. Specifying the type explicitly is not possible.
- var (x, b, y) = f();
- // Assigns to a pre-existing variable.
- (x, y) = (2, 7);
+ // Variables declared with type
+ uint x;
+ bool b;
+ uint y;
+ // Tuple values can be assigned to these pre-existing variables
+ (x, b, 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).
@@ -453,8 +455,9 @@ The ``require`` function should be used to ensure valid conditions, such as inpu
If used properly, analysis tools can evaluate your contract to identify the conditions and function calls which will reach a failing ``assert``. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix.
There are two other ways to trigger exceptions: The ``revert`` function can be used to flag an error and
-revert the current call. In the future it might be possible to also include details about the error
-in a call to ``revert``. The ``throw`` keyword can also be used as an alternative to ``revert()``.
+revert the current call. It is possible to provide a string message containing details about the error
+that will be passed back to the caller.
+The deprecated keyword ``throw`` can also be used as an alternative to ``revert()`` (but only without error message).
.. note::
From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future.
@@ -469,13 +472,16 @@ of an exception instead of "bubbling up".
Catching exceptions is not yet possible.
In the following example, you can see how ``require`` can be used to easily check conditions on inputs
-and how ``assert`` can be used for internal error checking::
+and how ``assert`` can be used for internal error checking. Note that you can optionally provide
+a message string for ``require``, but not for ``assert``.
- pragma solidity ^0.4.0;
+::
+
+ pragma solidity ^0.4.22;
contract Sharer {
function sendHalf(address addr) public payable returns (uint balance) {
- require(msg.value % 2 == 0); // Only allow even numbers
+ require(msg.value % 2 == 0, "Even value required.");
uint balanceBeforeTransfer = this.balance;
addr.transfer(msg.value / 2);
// Since transfer throws an exception on failure and
@@ -513,3 +519,33 @@ the EVM to revert all changes made to the state. The reason for reverting is tha
did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction
(or at least call) without effect. Note that ``assert``-style exceptions consume all gas available to the call, while
``require``-style exceptions will not consume any gas starting from the Metropolis release.
+
+The following example shows how an error string can be used together with revert and require:
+
+::
+
+ pragma solidity ^0.4.22;
+
+ contract VendingMachine {
+ function buy(uint amount) payable {
+ if (amount > msg.value / 2 ether)
+ revert("Not enough Ether provided.");
+ // Alternative way to do it:
+ require(
+ amount <= msg.value / 2 ether,
+ "Not enough Ether provided."
+ );
+ // Perform the purchase.
+ }
+ }
+
+The provided string will be :ref:`abi-encoded <ABI>` as if it were a call to a function ``Error(string)``.
+In the above example, ``revert("Not enough Ether provided.");`` will cause the following hexadecimal data be
+set as error return data:
+
+.. code::
+
+ 0x08c379a0 // Function selector for Error(string)
+ 0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset
+ 0x000000000000000000000000000000000000000000000000000000000000001a // String length
+ 0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data
diff --git a/docs/grammar.txt b/docs/grammar.txt
index a5c2acf3..b4ca5ca9 100644
--- a/docs/grammar.txt
+++ b/docs/grammar.txt
@@ -19,7 +19,7 @@ InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )*
StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )? Identifier ('=' Expression)? ';'
UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';'
StructDefinition = 'struct' Identifier '{'
- ( VariableDeclaration ';' (VariableDeclaration ';')* )? '}'
+ ( VariableDeclaration ';' (VariableDeclaration ';')* ) '}'
ModifierDefinition = 'modifier' Identifier ParameterList? Block
ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
diff --git a/docs/index.rst b/docs/index.rst
index f53b0fc4..80b0d6e7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -39,6 +39,7 @@ This documentation is translated into several languages by community volunteers,
* `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress)
* `Spanish <https://solidity-es.readthedocs.io>`_
* `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated)
+* `Korean <http://solidity-kr.readthedocs.io>`_ (in progress)
Useful links
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index e26870f0..6726ded9 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -122,7 +122,6 @@ We will re-add the pre-built bottles soon.
brew upgrade
brew tap ethereum/ethereum
brew install solidity
- brew linkapps solidity
If you need a specific version of Solidity you can install a
Homebrew formula directly from Github.
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index 56f0fe3e..84b1fff8 100644
--- a/docs/introduction-to-smart-contracts.rst
+++ b/docs/introduction-to-smart-contracts.rst
@@ -326,7 +326,13 @@ EVM bytecode and executed. The output of this execution is
permanently stored as the code of the contract.
This means that in order to create a contract, you do not
send the actual code of the contract, but in fact code that
-returns that code.
+returns that code when executed.
+
+.. note::
+ While a contract is being created, its code is still empty.
+ Because of that, you should not call back into the
+ contract under construction until its constructor has
+ finished executing.
.. index:: ! gas, ! gas price
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 075b6be0..c7c32528 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -64,12 +64,15 @@ The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25
Layout in Memory
****************
-Solidity reserves three 256-bit slots:
+Solidity reserves four 32 byte slots:
-- 0 - 64: scratch space for hashing methods
-- 64 - 96: currently allocated memory size (aka. free memory pointer)
+- ``0x00`` - ``0x3f``: scratch space for hashing methods
+- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer)
+- ``0x60`` - ``0x7f``: zero slot
-Scratch space can be used between statements (ie. within inline assembly).
+Scratch space can be used between statements (ie. within inline assembly). The zero slot
+is used as initial value for dynamic memory arrays and should never be written to
+(the free memory pointer points to ``0x80`` initially).
Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future).
@@ -314,7 +317,12 @@ The following is the order of precedence for operators, listed in order of evalu
Global Variables
================
-- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
+- ``abi.encode(...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
+- ``abi.encodePacked(...) returns (bytes)``: Performes :ref:`packed encoding <abi_packed_mode>` of the given arguments
+- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
+ starting from the second and prepends the given four-byte selector
+- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)```
+- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``.
- ``block.coinbase`` (``address``): current block miner's address
- ``block.difficulty`` (``uint``): current block difficulty
- ``block.gaslimit`` (``uint``): current block gaslimit
@@ -330,7 +338,10 @@ Global Variables
- ``tx.origin`` (``address``): sender of the transaction (full call chain)
- ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error)
- ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component)
+- ``require(bool condition, string message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message.
- ``revert()``: abort execution and revert state changes
+- ``revert(string message)``: abort execution and revert state changes providing an explanatory string
+- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
- ``sha3(...) returns (bytes32)``: an alias to ``keccak256``
- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>`
@@ -341,11 +352,28 @@ Global Variables
- ``this`` (current contract's type): the current contract, explicitly convertible to ``address``
- ``super``: the contract one level higher in the inheritance hierarchy
- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address
-- ``suicide(address recipient)``: an alias to ``selfdestruct``
+- ``suicide(address recipient)``: a deprecated alias to ``selfdestruct``
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei
- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure
- ``<address>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
+.. note::
+ Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
+ unless you know what you are doing.
+
+ Both the timestamp and the block hash can be influenced by miners to some degree.
+ Bad actors in the mining community can for example run a casino payout function on a chosen hash
+ and just retry a different hash if they did not receive any money.
+
+ The current block timestamp must be strictly larger than the timestamp of the last block,
+ but the only guarantee is that it will be somewhere between the timestamps of two
+ consecutive blocks in the canonical chain.
+
+.. note::
+ The block hashes are not available for all blocks for scalability reasons.
+ You can only access the hashes of the most recent 256 blocks, all other
+ values will be zero.
+
.. index:: visibility, public, private, external, internal
Function Visibility Specifiers
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index 27fefd49..546767e4 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -36,7 +36,7 @@ of votes.
::
- pragma solidity ^0.4.16;
+ pragma solidity ^0.4.22;
/// @title Voting with delegation.
contract Ballot {
@@ -87,18 +87,25 @@ of votes.
// Give `voter` the right to vote on this ballot.
// May only be called by `chairperson`.
function giveRightToVote(address voter) public {
- // If the argument of `require` evaluates to `false`,
- // it terminates and reverts all changes to
- // the state and to Ether balances. It is often
- // a good idea to use this if functions are
- // called incorrectly. But watch out, this
- // will currently also consume all provided gas
- // (this is planned to change in the future).
+ // If the first argument of `require` evaluates
+ // to `false`, execution terminates and all
+ // changes to the state and to Ether balances
+ // are reverted.
+ // This used to consume all gas in old EVM versions, but
+ // not anymore.
+ // It is often a good idea to use `require` to check if
+ // functions are called correctly.
+ // As a second argument, you can also provide an
+ // explanation about what went wrong.
require(
- (msg.sender == chairperson) &&
- !voters[voter].voted &&
- (voters[voter].weight == 0)
+ msg.sender == chairperson,
+ "Only chairperson can give right to vote."
);
+ require(
+ !voters[voter].voted,
+ "The voter already voted."
+ );
+ require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
@@ -106,10 +113,9 @@ of votes.
function delegate(address to) public {
// assigns reference
Voter storage sender = voters[msg.sender];
- require(!sender.voted);
+ require(!sender.voted, "You already voted.");
- // Self-delegation is not allowed.
- require(to != msg.sender);
+ require(to != msg.sender, "Self-delegation is disallowed.");
// Forward the delegation as long as
// `to` also delegated.
@@ -123,7 +129,7 @@ of votes.
to = voters[to].delegate;
// We found a loop in the delegation, not allowed.
- require(to != msg.sender);
+ require(to != msg.sender, "Found loop in delegation.");
}
// Since `sender` is a reference, this
@@ -146,7 +152,7 @@ of votes.
/// to proposal `proposals[proposal].name`.
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
- require(!sender.voted);
+ require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = proposal;
@@ -219,7 +225,7 @@ activate themselves.
::
- pragma solidity ^0.4.21;
+ pragma solidity ^0.4.22;
contract SimpleAuction {
// Parameters of the auction. Times are either
@@ -271,11 +277,17 @@ activate themselves.
// Revert the call if the bidding
// period is over.
- require(now <= auctionEnd);
+ require(
+ now <= auctionEnd,
+ "Auction already ended."
+ );
// If the bid is not higher, send the
// money back.
- require(msg.value > highestBid);
+ require(
+ msg.value > highestBid,
+ "There already is a higher bid."
+ );
if (highestBid != 0) {
// Sending back the money by simply using
@@ -325,8 +337,8 @@ activate themselves.
// external contracts.
// 1. Conditions
- require(now >= auctionEnd); // auction did not yet end
- require(!ended); // this function has already been called
+ require(now >= auctionEnd, "Auction not yet ended.");
+ require(!ended, "auctionEnd has already been called.");
// 2. Effects
ended = true;
@@ -376,7 +388,7 @@ high or low invalid bids.
::
- pragma solidity ^0.4.21;
+ pragma solidity ^0.4.22;
contract BlindAuction {
struct Bid {
@@ -529,7 +541,7 @@ Safe Remote Purchase
::
- pragma solidity ^0.4.21;
+ pragma solidity ^0.4.22;
contract Purchase {
uint public value;
@@ -544,7 +556,7 @@ Safe Remote Purchase
function Purchase() public payable {
seller = msg.sender;
value = msg.value / 2;
- require((2 * value) == msg.value);
+ require((2 * value) == msg.value, "Value has to be even.");
}
modifier condition(bool _condition) {
@@ -553,17 +565,26 @@ Safe Remote Purchase
}
modifier onlyBuyer() {
- require(msg.sender == buyer);
+ require(
+ msg.sender == buyer,
+ "Only buyer can call this."
+ );
_;
}
modifier onlySeller() {
- require(msg.sender == seller);
+ require(
+ msg.sender == seller,
+ "Only seller can call this."
+ );
_;
}
modifier inState(State _state) {
- require(state == _state);
+ require(
+ state == _state,
+ "Invalid state."
+ );
_;
}
diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst
index df40b1d0..d57f1703 100644
--- a/docs/structure-of-a-contract.rst
+++ b/docs/structure-of-a-contract.rst
@@ -62,13 +62,16 @@ Function modifiers can be used to amend the semantics of functions in a declarat
::
- pragma solidity ^0.4.11;
+ pragma solidity ^0.4.22;
contract Purchase {
address public seller;
modifier onlySeller() { // Modifier
- require(msg.sender == seller);
+ require(
+ msg.sender == seller,
+ "Only seller can call this."
+ );
_;
}
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
index 2261746f..ee1ea4bd 100644
--- a/docs/style-guide.rst
+++ b/docs/style-guide.rst
@@ -904,7 +904,7 @@ Constants
=========
Constants should be named with all capital letters with underscores separating
-words. Examples: ``MAX_BLOCKS``, `TOKEN_NAME`, ``TOKEN_TICKER``, ``CONTRACT_VERSION``.
+words. Examples: ``MAX_BLOCKS``, ``TOKEN_NAME``, ``TOKEN_TICKER``, ``CONTRACT_VERSION``.
Modifier Names
diff --git a/docs/types.rst b/docs/types.rst
index e704687e..5c20dc67 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -81,7 +81,7 @@ Fixed Point Numbers
``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by
the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive.
-``ufixed`` and ``fixed`` are aliases for ``ufixed128x19`` and ``fixed128x19``, respectively.
+``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively.
Operators:
@@ -179,8 +179,8 @@ All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-lev
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
.. note::
- All contracts inherit the members of address, so it is possible to query the balance of the
- current contract using ``this.balance``.
+ All contracts can be converted to ``address`` type, so it is possible to query the balance of the
+ current contract using ``address(this).balance``.
.. note::
The use of ``callcode`` is discouraged and will be removed in the future.
@@ -470,7 +470,7 @@ Example that shows how to use internal function types::
Another example that uses external function types::
- pragma solidity ^0.4.21;
+ pragma solidity ^0.4.22;
contract Oracle {
struct Request {
@@ -495,7 +495,10 @@ Another example that uses external function types::
oracle.query("USD", this.oracleResponse);
}
function oracleResponse(bytes response) public {
- require(msg.sender == address(oracle));
+ require(
+ msg.sender == address(oracle),
+ "Only oracle can call this."
+ );
// Use the data
}
}
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index 1b58b1e8..51f7b9f3 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -44,15 +44,16 @@ Special Variables and Functions
===============================
There are special variables and functions which always exist in the global
-namespace and are mainly used to provide information about the blockchain.
+namespace and are mainly used to provide information about the blockchain
+or are general-use utility functions.
-.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin
+.. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin
Block and Transaction Properties
--------------------------------
-- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks excluding current
+- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``.
- ``block.coinbase`` (``address``): current block miner's address
- ``block.difficulty`` (``uint``): current block difficulty
- ``block.gaslimit`` (``uint``): current block gaslimit
@@ -74,7 +75,7 @@ Block and Transaction Properties
This includes calls to library functions.
.. note::
- Do not rely on ``block.timestamp``, ``now`` and ``block.blockhash`` as a source of randomness,
+ Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
unless you know what you are doing.
Both the timestamp and the block hash can be influenced by miners to some degree.
@@ -90,6 +91,26 @@ Block and Transaction Properties
You can only access the hashes of the most recent 256 blocks, all other
values will be zero.
+.. index:: abi, encoding, packed
+
+ABI Encoding Functions
+----------------------
+
+- ``abi.encode(...) returns (bytes)``: ABI-encodes the given arguments
+- ``abi.encodePacked(...) returns (bytes)``: Performes packed encoding of the given arguments
+- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: ABI-encodes the given arguments
+ starting from the second and prepends the given four-byte selector
+- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)```
+
+.. note::
+ These encoding functions can be used to craft data for function calls without actually
+ calling a function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a more
+ explicit way to compute ``keccak256(a, b)``, which will be deprecated in future
+ versions.
+
+See the documentation about the :ref:`ABI <ABI>` and the
+:ref:`tightly packed encoding <abi_packed_mode>` for details about the encoding.
+
.. index:: assert, revert, require
Error Handling
@@ -99,8 +120,12 @@ Error Handling
throws 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.
+``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.
``revert()``:
abort execution and revert state changes
+``revert(string reason)``:
+ abort execution and revert state changes, providing an explanatory string
.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography,
@@ -169,6 +194,13 @@ For more information, see the section on :ref:`address`.
Use a pattern where the recipient withdraws the money.
.. note::
+ If storage variables are accessed via a low-level delegatecall, the storage layout of the two contracts
+ must align in order for the called contract to correctly access the storage variables of the calling contract by name.
+ This is of course not the case if storage pointers are passed as function arguments as in the case for
+ the high-level libraries.
+
+
+.. note::
The use of ``callcode`` is discouraged and will be removed in the future.
.. index:: this, selfdestruct
@@ -183,7 +215,7 @@ Contract Related
destroy the current contract, sending its funds to the given :ref:`address`
``suicide(address recipient)``:
- alias to ``selfdestruct``
+ deprecated alias to ``selfdestruct``
Furthermore, all functions of the current contract are callable directly including the current function.
diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h
new file mode 100644
index 00000000..b2540668
--- /dev/null
+++ b/libdevcore/Algorithms.h
@@ -0,0 +1,76 @@
+/*
+ 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 <functional>
+#include <set>
+
+namespace dev
+{
+
+/**
+ * Detector for cycles in directed graphs. It returns the first
+ * vertex on the path towards a cycle or a nullptr if there is
+ * no reachable cycle starting from a given vertex.
+ */
+template <typename V>
+class CycleDetector
+{
+public:
+ /// Initializes the cycle detector
+ /// @param _visit function that is given the current vertex
+ /// and is supposed to call @a run on all
+ /// adjacent vertices.
+ explicit CycleDetector(std::function<void(V const&, CycleDetector&)> _visit):
+ m_visit(std::move(_visit))
+ { }
+
+ /// Recursively perform cycle detection starting
+ /// (or continuing) with @param _vertex
+ /// @returns the first vertex on the path towards a cycle from @a _vertex
+ /// or nullptr if no cycle is reachable from @a _vertex.
+ V const* run(V const& _vertex)
+ {
+ if (m_firstCycleVertex)
+ return m_firstCycleVertex;
+ if (m_processed.count(&_vertex))
+ return nullptr;
+ else if (m_processing.count(&_vertex))
+ return m_firstCycleVertex = &_vertex;
+ m_processing.insert(&_vertex);
+
+ m_depth++;
+ m_visit(_vertex, *this);
+ m_depth--;
+ if (m_firstCycleVertex && m_depth == 1)
+ m_firstCycleVertex = &_vertex;
+
+ m_processing.erase(&_vertex);
+ m_processed.insert(&_vertex);
+ return m_firstCycleVertex;
+ }
+
+private:
+ std::function<void(V const&, CycleDetector&)> m_visit;
+ std::set<V const*> m_processing;
+ std::set<V const*> m_processed;
+ size_t m_depth = 0;
+ V const* m_firstCycleVertex = nullptr;
+};
+
+}
diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt
index d107f701..fa7e3f48 100644
--- a/libdevcore/CMakeLists.txt
+++ b/libdevcore/CMakeLists.txt
@@ -2,9 +2,7 @@ file(GLOB sources "*.cpp")
file(GLOB headers "*.h")
add_library(devcore ${sources} ${headers})
-target_link_libraries(devcore PRIVATE ${JSONCPP_LIBRARY} ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
-target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
+target_link_libraries(devcore PRIVATE jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
-target_include_directories(devcore PUBLIC "${JSONCPP_INCLUDE_DIR}")
-add_dependencies(devcore jsoncpp)
+target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
add_dependencies(devcore solidity_BuildInfo.h)
diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp
index 8c7e08f6..0063a8d4 100644
--- a/libdevcore/CommonIO.cpp
+++ b/libdevcore/CommonIO.cpp
@@ -27,6 +27,7 @@
#if defined(_WIN32)
#include <windows.h>
#else
+#include <unistd.h>
#include <termios.h>
#endif
#include <boost/filesystem.hpp>
@@ -118,3 +119,71 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDe
}
}
}
+
+#if defined(_WIN32)
+class DisableConsoleBuffering
+{
+public:
+ DisableConsoleBuffering()
+ {
+ m_stdin = GetStdHandle(STD_INPUT_HANDLE);
+ GetConsoleMode(m_stdin, &m_oldMode);
+ SetConsoleMode(m_stdin, m_oldMode & (~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)));
+ }
+ ~DisableConsoleBuffering()
+ {
+ SetConsoleMode(m_stdin, m_oldMode);
+ }
+private:
+ HANDLE m_stdin;
+ DWORD m_oldMode;
+};
+#else
+class DisableConsoleBuffering
+{
+public:
+ DisableConsoleBuffering()
+ {
+ tcgetattr(0, &m_termios);
+ m_termios.c_lflag &= ~ICANON;
+ m_termios.c_lflag &= ~ECHO;
+ m_termios.c_cc[VMIN] = 1;
+ m_termios.c_cc[VTIME] = 0;
+ tcsetattr(0, TCSANOW, &m_termios);
+ }
+ ~DisableConsoleBuffering()
+ {
+ m_termios.c_lflag |= ICANON;
+ m_termios.c_lflag |= ECHO;
+ tcsetattr(0, TCSADRAIN, &m_termios);
+ }
+private:
+ struct termios m_termios;
+};
+#endif
+
+int dev::readStandardInputChar()
+{
+ DisableConsoleBuffering disableConsoleBuffering;
+ return cin.get();
+}
+
+boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path)
+{
+ if (boost::filesystem::exists(_path))
+ return boost::filesystem::canonical(_path);
+ else
+ {
+ boost::filesystem::path head(_path);
+ boost::filesystem::path tail;
+ for (auto it = --_path.end(); !head.empty(); --it)
+ {
+ if (boost::filesystem::exists(head))
+ break;
+ tail = (*it) / tail;
+ head.remove_filename();
+ }
+ head = boost::filesystem::canonical(head);
+ return head / tail;
+ }
+}
diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h
index 33769ec3..9ba68e74 100644
--- a/libdevcore/CommonIO.h
+++ b/libdevcore/CommonIO.h
@@ -25,6 +25,7 @@
#include <sstream>
#include <string>
+#include <boost/filesystem.hpp>
#include "Common.h"
namespace dev
@@ -37,6 +38,9 @@ std::string readFileAsString(std::string const& _file);
/// Retrieve and returns the contents of standard input (until EOF).
std::string readStandardInput();
+/// Retrieve and returns a character from standard input (without waiting for EOL).
+int readStandardInputChar();
+
/// Write the given binary data into the given file, replacing the file if it pre-exists.
/// Throws exception on error.
/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
@@ -54,4 +58,8 @@ std::string toString(_T const& _t)
return o.str();
}
+/// Partial implementation of boost::filesystem::weakly_canonical (available in boost>=1.60).
+/// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped.
+boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path);
+
}
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index bd4ebf59..b71bc80c 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -438,13 +438,15 @@ map<u256, u256> Assembly::optimiseInternal(
// function types that can be stored in storage.
AssemblyItems optimisedItems;
+ bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem(Instruction::MSIZE)) != m_items.end());
+
auto iter = m_items.begin();
while (iter != m_items.end())
{
KnownState emptyState;
CommonSubexpressionEliminator eliminator(emptyState);
auto orig = iter;
- iter = eliminator.feedItems(iter, m_items.end());
+ iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
bool shouldReplace = false;
AssemblyItems optimisedChunk;
try
diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h
index 0b957a0e..b20de246 100644
--- a/libevmasm/CommonSubexpressionEliminator.h
+++ b/libevmasm/CommonSubexpressionEliminator.h
@@ -65,8 +65,9 @@ public:
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
/// item that must be fed into a new instance of the eliminator.
+ /// @param _msizeImportant if false, do not consider modification of MSIZE a side-effect
template <class _AssemblyItemIterator>
- _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end);
+ _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end, bool _msizeImportant);
/// @returns the resulting items after optimization.
AssemblyItems getOptimizedItems();
@@ -168,11 +169,12 @@ private:
template <class _AssemblyItemIterator>
_AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
_AssemblyItemIterator _iterator,
- _AssemblyItemIterator _end
+ _AssemblyItemIterator _end,
+ bool _msizeImportant
)
{
assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator.");
- for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator)
+ for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant); ++_iterator)
feedItem(*_iterator);
if (_iterator != _end)
m_breakingItem = &(*_iterator++);
diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp
index a677a631..f9bbad2c 100644
--- a/libevmasm/Instruction.cpp
+++ b/libevmasm/Instruction.cpp
@@ -199,7 +199,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } },
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } },
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } },
- { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, false, Tier::Special } },
+ { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, true, Tier::Special } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Balance } },
{ Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } },
diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp
index 168d1109..8a39de24 100644
--- a/libevmasm/PeepholeOptimiser.cpp
+++ b/libevmasm/PeepholeOptimiser.cpp
@@ -154,6 +154,51 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush, 2>
}
};
+struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap, 2>
+{
+ static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
+ {
+ // Remove SWAP1 if following instruction is commutative
+ if (
+ _swap.type() == Operation &&
+ _swap.instruction() == Instruction::SWAP1 &&
+ SemanticInformation::isCommutativeOperation(_op)
+ )
+ {
+ *_out = _op;
+ return true;
+ }
+ else
+ return false;
+ }
+};
+
+struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
+{
+ static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
+ {
+ map<Instruction, Instruction> swappableOps{
+ { Instruction::LT, Instruction::GT },
+ { Instruction::GT, Instruction::LT },
+ { Instruction::SLT, Instruction::SGT },
+ { Instruction::SGT, Instruction::SLT }
+ };
+
+ if (
+ _swap.type() == Operation &&
+ _swap.instruction() == Instruction::SWAP1 &&
+ _op.type() == Operation &&
+ swappableOps.count(_op.instruction())
+ )
+ {
+ *_out = swappableOps.at(_op.instruction());
+ return true;
+ }
+ else
+ return false;
+ }
+};
+
struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext, 3>
{
static size_t applySimple(
@@ -260,7 +305,7 @@ bool PeepholeOptimiser::optimise()
{
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
while (state.i < m_items.size())
- applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
+ applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
if (m_optimisedItems.size() < m_items.size() || (
m_optimisedItems.size() == m_items.size() && (
eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) ||
diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h
index da522cec..abcf170c 100644
--- a/libevmasm/RuleList.h
+++ b/libevmasm/RuleList.h
@@ -89,6 +89,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
u256 mask = (u256(1) << testBit) - 1;
return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask);
}, false},
+ {{Instruction::SHL, {A, B}}, [=]{
+ if (A.d() > 255)
+ return u256(0);
+ return u256(bigint(B.d()) << unsigned(A.d()));
+ }, false},
+ {{Instruction::SHR, {A, B}}, [=]{
+ if (A.d() > 255)
+ return u256(0);
+ return B.d() >> unsigned(A.d());
+ }, false},
// invariants involving known constants
{{Instruction::ADD, {X, 0}}, [=]{ return X; }, false},
diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp
index 03870f7c..71267ee8 100644
--- a/libevmasm/SemanticInformation.cpp
+++ b/libevmasm/SemanticInformation.cpp
@@ -28,7 +28,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
-bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
+bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
{
switch (_item.type())
{
@@ -59,6 +59,11 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
return false;
if (_item.instruction() == Instruction::MSTORE)
return false;
+ if (!_msizeImportant && (
+ _item.instruction() == Instruction::MLOAD ||
+ _item.instruction() == Instruction::KECCAK256
+ ))
+ return false;
//@todo: We do not handle the following memory instructions for now:
// calldatacopy, codecopy, extcodecopy, mstore8,
// msize (note that msize also depends on memory read access)
diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h
index 83656252..8bdc70be 100644
--- a/libevmasm/SemanticInformation.h
+++ b/libevmasm/SemanticInformation.h
@@ -38,7 +38,8 @@ class AssemblyItem;
struct SemanticInformation
{
/// @returns true if the given items starts a new block for common subexpression analysis.
- static bool breaksCSEAnalysisBlock(AssemblyItem const& _item);
+ /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize.
+ static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant);
/// @returns true if the item is a two-argument operation whose value does not depend on the
/// order of its arguments.
static bool isCommutativeOperation(AssemblyItem const& _item);
diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.cpp b/libjulia/optimiser/CommonSubexpressionEliminator.cpp
new file mode 100644
index 00000000..229bd35e
--- /dev/null
+++ b/libjulia/optimiser/CommonSubexpressionEliminator.cpp
@@ -0,0 +1,48 @@
+/*(
+ 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/>.
+*/
+/**
+ * Optimisation stage that replaces expressions known to be the current value of a variable
+ * in scope by a reference to that variable.
+ */
+
+#include <libjulia/optimiser/CommonSubexpressionEliminator.h>
+
+#include <libjulia/optimiser/Metrics.h>
+#include <libjulia/optimiser/SyntacticalEquality.h>
+
+#include <libsolidity/inlineasm/AsmData.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+
+void CommonSubexpressionEliminator::visit(Expression& _e)
+{
+ // Single exception for substitution: We do not substitute one variable for another.
+ if (_e.type() != typeid(Identifier))
+ // TODO this search rather inefficient.
+ for (auto const& var: m_value)
+ {
+ solAssert(var.second, "");
+ if (SyntacticalEqualityChecker::equal(_e, *var.second))
+ {
+ _e = Identifier{locationOf(_e), var.first};
+ break;
+ }
+ }
+ DataFlowAnalyzer::visit(_e);
+}
diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.h b/libjulia/optimiser/CommonSubexpressionEliminator.h
new file mode 100644
index 00000000..a8ca3abb
--- /dev/null
+++ b/libjulia/optimiser/CommonSubexpressionEliminator.h
@@ -0,0 +1,45 @@
+/*
+ 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/>.
+*/
+/**
+ * Optimisation stage that replaces expressions known to be the current value of a variable
+ * in scope by a reference to that variable.
+ */
+
+#pragma once
+
+#include <libjulia/optimiser/DataFlowAnalyzer.h>
+
+namespace dev
+{
+namespace julia
+{
+
+/**
+ * Optimisation stage that replaces expressions known to be the current value of a variable
+ * in scope by a reference to that variable.
+ *
+ * Prerequisite: Disambiguator
+ */
+class CommonSubexpressionEliminator: public DataFlowAnalyzer
+{
+protected:
+ using ASTModifier::visit;
+ virtual void visit(Expression& _e) override;
+};
+
+}
+}
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index 83f37f47..8659bbfd 100644
--- a/libsolidity/analysis/ConstantEvaluator.cpp
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -87,6 +87,12 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
setType(_identifier, type(*value));
}
+void ConstantEvaluator::endVisit(TupleExpression const& _tuple)
+{
+ if (!_tuple.isInlineArray() && _tuple.components().size() == 1)
+ setType(_tuple, type(*_tuple.components().front()));
+}
+
void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type)
{
if (_type && _type->category() == Type::Category::RationalNumber)
diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h
index 77a357b6..ac3a24a1 100644
--- a/libsolidity/analysis/ConstantEvaluator.h
+++ b/libsolidity/analysis/ConstantEvaluator.h
@@ -56,6 +56,7 @@ private:
virtual void endVisit(UnaryOperation const& _operation);
virtual void endVisit(Literal const& _literal);
virtual void endVisit(Identifier const& _identifier);
+ virtual void endVisit(TupleExpression const& _tuple);
void setType(ASTNode const& _node, TypePointer const& _type);
TypePointer type(ASTNode const& _node);
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index c7ba78d6..786272e4 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -45,7 +45,8 @@ Declaration const* DeclarationContainer::conflictingDeclaration(
if (
dynamic_cast<FunctionDefinition const*>(&_declaration) ||
- dynamic_cast<EventDefinition const*>(&_declaration)
+ dynamic_cast<EventDefinition const*>(&_declaration) ||
+ dynamic_cast<MagicVariableDeclaration const*>(&_declaration)
)
{
// check that all other declarations with the same name are functions or a public state variable or events.
@@ -68,6 +69,11 @@ Declaration const* DeclarationContainer::conflictingDeclaration(
!dynamic_cast<EventDefinition const*>(declaration)
)
return declaration;
+ if (
+ dynamic_cast<MagicVariableDeclaration const*>(&_declaration) &&
+ !dynamic_cast<MagicVariableDeclaration const*>(declaration)
+ )
+ return declaration;
// Or, continue.
}
}
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index 34cb61d8..756bb540 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -35,9 +35,11 @@ namespace solidity
GlobalContext::GlobalContext():
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
+ make_shared<MagicVariableDeclaration>("abi", make_shared<MagicType>(MagicType::Kind::ABI)),
make_shared<MagicVariableDeclaration>("addmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
+ make_shared<MagicVariableDeclaration>("blockhash", make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
@@ -50,7 +52,9 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("mulmod", make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)),
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 2f675135..0a356f04 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -47,7 +47,9 @@ NameAndTypeResolver::NameAndTypeResolver(
if (!m_scopes[nullptr])
m_scopes[nullptr].reset(new DeclarationContainer());
for (Declaration const* declaration: _globals)
- m_scopes[nullptr]->registerDeclaration(*declaration);
+ {
+ solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration.");
+ }
}
bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope)
@@ -202,8 +204,9 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
solAssert(
dynamic_cast<FunctionDefinition const*>(declaration) ||
dynamic_cast<EventDefinition const*>(declaration) ||
- dynamic_cast<VariableDeclaration const*>(declaration),
- "Found overloading involving something not a function or a variable."
+ dynamic_cast<VariableDeclaration const*>(declaration) ||
+ dynamic_cast<MagicVariableDeclaration const*>(declaration),
+ "Found overloading involving something not a function, event or a (magic) variable."
);
FunctionTypePointer functionType { declaration->functionType(false) };
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index fbc72e52..19d0b708 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -21,6 +21,8 @@
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
+#include <libdevcore/Algorithms.h>
+
#include <boost/range/adaptor/map.hpp>
#include <memory>
@@ -47,7 +49,7 @@ void PostTypeChecker::endVisit(ContractDefinition const&)
{
solAssert(!m_currentConstVariable, "");
for (auto declaration: m_constVariables)
- if (auto identifier = findCycle(declaration))
+ if (auto identifier = findCycle(*declaration))
m_errorReporter.typeError(
declaration->location(),
"The value of the constant " + declaration->name() +
@@ -87,20 +89,24 @@ bool PostTypeChecker::visit(Identifier const& _identifier)
return true;
}
-VariableDeclaration const* PostTypeChecker::findCycle(
- VariableDeclaration const* _startingFrom,
- set<VariableDeclaration const*> const& _seen
-)
+VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom)
{
- if (_seen.count(_startingFrom))
- return _startingFrom;
- else if (m_constVariableDependencies.count(_startingFrom))
+ auto visitor = [&](VariableDeclaration const& _variable, CycleDetector<VariableDeclaration>& _cycleDetector)
{
- set<VariableDeclaration const*> seen(_seen);
- seen.insert(_startingFrom);
- for (auto v: m_constVariableDependencies[_startingFrom])
- if (findCycle(v, seen))
- return v;
- }
- return nullptr;
+ // Iterating through the dependencies needs to be deterministic and thus cannot
+ // depend on the memory layout.
+ // Because of that, we sort by AST node id.
+ vector<VariableDeclaration const*> dependencies(
+ m_constVariableDependencies[&_variable].begin(),
+ m_constVariableDependencies[&_variable].end()
+ );
+ sort(dependencies.begin(), dependencies.end(), [](VariableDeclaration const* _a, VariableDeclaration const* _b) -> bool
+ {
+ return _a->id() < _b->id();
+ });
+ for (auto v: dependencies)
+ if (_cycleDetector.run(*v))
+ return;
+ };
+ return CycleDetector<VariableDeclaration>(visitor).run(_startingFrom);
}
diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h
index bafc1ae6..4f9dac6e 100644
--- a/libsolidity/analysis/PostTypeChecker.h
+++ b/libsolidity/analysis/PostTypeChecker.h
@@ -55,10 +55,7 @@ private:
virtual bool visit(Identifier const& _identifier) override;
- VariableDeclaration const* findCycle(
- VariableDeclaration const* _startingFrom,
- std::set<VariableDeclaration const*> const& _seen = std::set<VariableDeclaration const*>{}
- );
+ VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom);
ErrorReporter& m_errorReporter;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index d4de219a..00a581d0 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/StaticAnalyzer.h>
+#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <memory>
@@ -50,6 +51,16 @@ void StaticAnalyzer::endVisit(ContractDefinition const&)
bool StaticAnalyzer::visit(FunctionDefinition const& _function)
{
+ const bool isInterface = m_currentContract->contractKind() == ContractDefinition::ContractKind::Interface;
+
+ if (_function.noVisibilitySpecified())
+ m_errorReporter.warning(
+ _function.location(),
+ "No visibility specified. Defaulting to \"" +
+ Declaration::visibilityToString(_function.visibility()) +
+ "\". " +
+ (isInterface ? "In interfaces it defaults to external." : "")
+ );
if (_function.isImplemented())
m_currentFunction = &_function;
else
@@ -68,13 +79,13 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
for (auto const& var: m_localVarUseCount)
if (var.second == 0)
{
- if (var.first->isCallableParameter())
+ if (var.first.second->isCallableParameter())
m_errorReporter.warning(
- var.first->location(),
+ var.first.second->location(),
"Unused function parameter. Remove or comment out the variable name to silence this warning."
);
else
- m_errorReporter.warning(var.first->location(), "Unused local variable.");
+ m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
}
m_localVarUseCount.clear();
@@ -87,7 +98,7 @@ bool StaticAnalyzer::visit(Identifier const& _identifier)
{
solAssert(!var->name().empty(), "");
if (var->isLocalVariable())
- m_localVarUseCount[var] += 1;
+ m_localVarUseCount[make_pair(var->id(), var)] += 1;
}
return true;
}
@@ -99,7 +110,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
solAssert(_variable.isLocalVariable(), "");
if (_variable.name() != "")
// This is not a no-op, the entry might pre-exist.
- m_localVarUseCount[&_variable] += 0;
+ m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0;
}
else if (_variable.isStateVariable())
{
@@ -122,7 +133,7 @@ bool StaticAnalyzer::visit(Return const& _return)
if (m_currentFunction && _return.expression())
for (auto const& var: m_currentFunction->returnParameters())
if (!var->name().empty())
- m_localVarUseCount[var.get()] += 1;
+ m_localVarUseCount[make_pair(var->id(), var.get())] += 1;
return true;
}
@@ -142,6 +153,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
+ {
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
{
if (v050)
@@ -155,6 +167,20 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
);
}
+ if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
+ {
+ if (v050)
+ m_errorReporter.typeError(
+ _memberAccess.location(),
+ "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
+ );
+ else
+ m_errorReporter.warning(
+ _memberAccess.location(),
+ "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
+ );
+ }
+ }
if (m_nonPayablePublic && !m_library)
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
@@ -180,10 +206,32 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
);
}
- if (m_constructor && m_currentContract)
- if (ContractType const* type = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type.get()))
- if (type->contractDefinition() == *m_currentContract)
- m_errorReporter.warning(_memberAccess.location(), "\"this\" used in constructor.");
+ if (m_constructor)
+ {
+ auto const* expr = &_memberAccess.expression();
+ while(expr)
+ {
+ if (auto id = dynamic_cast<Identifier const*>(expr))
+ {
+ if (id->name() == "this")
+ m_errorReporter.warning(
+ id->location(),
+ "\"this\" used in constructor. "
+ "Note that external functions of a contract "
+ "cannot be called while it is being constructed.");
+ break;
+ }
+ else if (auto tuple = dynamic_cast<TupleExpression const*>(expr))
+ {
+ if (tuple->components().size() == 1)
+ expr = tuple->components().front().get();
+ else
+ break;
+ }
+ else
+ break;
+ }
+ }
return true;
}
@@ -199,13 +247,54 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly)
{
solAssert(!var->name().empty(), "");
if (var->isLocalVariable())
- m_localVarUseCount[var] += 1;
+ m_localVarUseCount[make_pair(var->id(), var)] += 1;
}
}
return true;
}
+bool StaticAnalyzer::visit(BinaryOperation const& _operation)
+{
+ if (
+ _operation.rightExpression().annotation().isPure &&
+ (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod)
+ )
+ if (auto rhs = dynamic_pointer_cast<RationalNumberType const>(
+ ConstantEvaluator(m_errorReporter).evaluate(_operation.rightExpression())
+ ))
+ if (rhs->isZero())
+ m_errorReporter.typeError(
+ _operation.location(),
+ (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
+ );
+
+ return true;
+}
+
+bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
+{
+ if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall)
+ {
+ auto functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type);
+ solAssert(functionType, "");
+ if (functionType->kind() == FunctionType::Kind::AddMod || functionType->kind() == FunctionType::Kind::MulMod)
+ {
+ solAssert(_functionCall.arguments().size() == 3, "");
+ if (_functionCall.arguments()[2]->annotation().isPure)
+ if (auto lastArg = dynamic_pointer_cast<RationalNumberType const>(
+ ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2])
+ ))
+ if (lastArg->isZero())
+ m_errorReporter.typeError(
+ _functionCall.location(),
+ "Arithmetic modulo zero."
+ );
+ }
+ }
+ return true;
+}
+
bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set<StructDefinition const*>& _structsSeen)
{
switch (_type.category())
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 124c4e7c..2a62e391 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -64,6 +64,8 @@ private:
virtual bool visit(Return const& _return) override;
virtual bool visit(MemberAccess const& _memberAccess) override;
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
+ virtual bool visit(BinaryOperation const& _operation) override;
+ virtual bool visit(FunctionCall const& _functionCall) override;
/// @returns the size of this type in storage, including all sub-types.
static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen);
@@ -77,7 +79,9 @@ private:
bool m_nonPayablePublic = false;
/// Number of uses of each (named) local variable in a function, counter is initialized with zero.
- std::map<VariableDeclaration const*, int> m_localVarUseCount;
+ /// Pairs of AST ids and pointers are used as keys to ensure a deterministic order
+ /// when traversing.
+ std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount;
FunctionDefinition const* m_currentFunction = nullptr;
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index ddac194b..f648e5b4 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -214,18 +214,31 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
- if (_function.noVisibilitySpecified())
+ if (v050 && _function.noVisibilitySpecified())
+ m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
+
+ if (_function.isOldStyleConstructor())
{
if (v050)
- m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
+ m_errorReporter.syntaxError(
+ _function.location(),
+ "Functions are not allowed to have the same name as the contract. "
+ "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
+ );
else
m_errorReporter.warning(
_function.location(),
- "No visibility specified. Defaulting to \"" +
- Declaration::visibilityToString(_function.visibility()) +
- "\"."
+ "Defining constructors as functions with the same name as the contract is deprecated. "
+ "Use \"constructor(...) { ... }\" instead."
);
}
+ if (!_function.isImplemented() && !_function.modifiers().empty())
+ {
+ if (v050)
+ m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers.");
+ else
+ m_errorReporter.warning( _function.location(), "Modifiers of functions without implementation are ignored." );
+ }
return true;
}
@@ -255,3 +268,17 @@ bool SyntaxChecker::visit(VariableDeclaration const& _declaration)
}
return true;
}
+
+bool SyntaxChecker::visit(StructDefinition const& _struct)
+{
+ bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (_struct.members().empty())
+ {
+ if (v050)
+ m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed.");
+ else
+ m_errorReporter.warning(_struct.location(), "Defining empty structs is deprecated.");
+ }
+ return true;
+}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 871bf0a9..1579df57 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -71,6 +71,8 @@ private:
virtual bool visit(VariableDeclaration const& _declaration) override;
+ virtual bool visit(StructDefinition const& _struct) override;
+
ErrorReporter& m_errorReporter;
/// Flag that indicates whether a function modifier actually contains '_'.
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index bebdb9b6..47a551dc 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -60,17 +60,7 @@ bool typeSupportedByOldABIEncoder(Type const& _type)
bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
{
- try
- {
- _contract.accept(*this);
- }
- catch (FatalError const&)
- {
- // We got a fatal error which required to stop further type checking, but we can
- // continue normally from here.
- if (m_errorReporter.errors().empty())
- throw; // Something is weird here, rather throw again.
- }
+ _contract.accept(*this);
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
@@ -101,7 +91,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
checkContractDuplicateEvents(_contract);
checkContractIllegalOverrides(_contract);
checkContractAbstractFunctions(_contract);
- checkContractAbstractConstructors(_contract);
+ checkContractBaseConstructorArguments(_contract);
FunctionDefinition const* function = _contract.constructor();
if (function)
@@ -291,42 +281,108 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont
}
}
-void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract)
+void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract)
{
- set<ContractDefinition const*> argumentsNeeded;
- // check that we get arguments for all base constructors that need it.
- // If not mark the contract as abstract (not fully implemented)
+ bool const v050 = _contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
- for (ContractDefinition const* contract: bases)
- if (FunctionDefinition const* constructor = contract->constructor())
- if (contract != &_contract && !constructor->parameters().empty())
- argumentsNeeded.insert(contract);
+ // Determine the arguments that are used for the base constructors.
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->constructor())
for (auto const& modifier: constructor->modifiers())
{
- auto baseContract = dynamic_cast<ContractDefinition const*>(
- &dereference(*modifier->name())
- );
- if (baseContract)
- argumentsNeeded.erase(baseContract);
+ auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name()));
+ if (modifier->arguments())
+ {
+ if (baseContract && baseContract->constructor())
+ annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
+ }
+ else
+ {
+ if (v050)
+ m_errorReporter.declarationError(
+ modifier->location(),
+ "Modifier-style base constructor call without arguments."
+ );
+ else
+ m_errorReporter.warning(
+ modifier->location(),
+ "Modifier-style base constructor call without arguments."
+ );
+ }
}
-
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
solAssert(baseContract, "");
- if (!base->arguments().empty())
- argumentsNeeded.erase(baseContract);
+
+ if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
+ annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
+ }
+ }
+
+ // check that we get arguments for all base constructors that need it.
+ // If not mark the contract as abstract (not fully implemented)
+ for (ContractDefinition const* contract: bases)
+ if (FunctionDefinition const* constructor = contract->constructor())
+ if (contract != &_contract && !constructor->parameters().empty())
+ if (!_contract.annotation().baseConstructorArguments.count(constructor))
+ _contract.annotation().unimplementedFunctions.push_back(constructor);
+}
+
+void TypeChecker::annotateBaseConstructorArguments(
+ ContractDefinition const& _currentContract,
+ FunctionDefinition const* _baseConstructor,
+ ASTNode const* _argumentNode
+)
+{
+ bool const v050 = _currentContract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ solAssert(_baseConstructor, "");
+ solAssert(_argumentNode, "");
+
+ auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
+ std::make_pair(_baseConstructor, _argumentNode)
+ );
+ if (!insertionResult.second)
+ {
+ ASTNode const* previousNode = insertionResult.first->second;
+
+ SourceLocation const* mainLocation = nullptr;
+ SecondarySourceLocation ssl;
+
+ if (
+ _currentContract.location().contains(previousNode->location()) ||
+ _currentContract.location().contains(_argumentNode->location())
+ )
+ {
+ mainLocation = &previousNode->location();
+ ssl.append("Second constructor call is here:", _argumentNode->location());
}
+ else
+ {
+ mainLocation = &_currentContract.location();
+ ssl.append("First constructor call is here: ", _argumentNode->location());
+ ssl.append("Second constructor call is here: ", previousNode->location());
+ }
+
+ if (v050)
+ m_errorReporter.declarationError(
+ *mainLocation,
+ ssl,
+ "Base constructor arguments given twice."
+ );
+ else
+ m_errorReporter.warning(
+ *mainLocation,
+ "Base constructor arguments given twice.",
+ ssl
+ );
}
- if (!argumentsNeeded.empty())
- for (ContractDefinition const* contract: argumentsNeeded)
- _contract.annotation().unimplementedFunctions.push_back(contract->constructor());
+
}
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
@@ -378,7 +434,16 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func
function.annotation().superFunction = &super;
if (function.visibility() != super.visibility())
+ {
+ // visibility is enforced to be external in interfaces, but a contract can override that with public
+ if (
+ super.inContractKind() == ContractDefinition::ContractKind::Interface &&
+ function.inContractKind() != ContractDefinition::ContractKind::Interface &&
+ function.visibility() == FunctionDefinition::Visibility::Public
+ )
+ return;
overrideError(function, super, "Overriding function visibility differs.");
+ }
else if (function.stateMutability() != super.stateMutability())
overrideError(
@@ -497,30 +562,46 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
// Interfaces do not have constructors, so there are zero parameters.
parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
- if (!arguments.empty() && parameterTypes.size() != arguments.size())
+ if (arguments)
{
- m_errorReporter.typeError(
- _inheritance.location(),
- "Wrong argument count for constructor call: " +
- toString(arguments.size()) +
- " arguments given but expected " +
- toString(parameterTypes.size()) +
- "."
- );
- return;
- }
+ bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
- for (size_t i = 0; i < arguments.size(); ++i)
- if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
- m_errorReporter.typeError(
- arguments[i]->location(),
- "Invalid type for argument in constructor call. "
- "Invalid implicit conversion from " +
- type(*arguments[i])->toString() +
- " to " +
- parameterTypes[i]->toString() +
- " requested."
- );
+ if (parameterTypes.size() != arguments->size())
+ {
+ if (arguments->size() == 0 && !v050)
+ m_errorReporter.warning(
+ _inheritance.location(),
+ "Wrong argument count for constructor call: " +
+ toString(arguments->size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ "."
+ );
+ else
+ {
+ m_errorReporter.typeError(
+ _inheritance.location(),
+ "Wrong argument count for constructor call: " +
+ toString(arguments->size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ "."
+ );
+ return;
+ }
+ }
+ for (size_t i = 0; i < arguments->size(); ++i)
+ if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ m_errorReporter.typeError(
+ (*arguments)[i]->location(),
+ "Invalid type for argument in constructor call. "
+ "Invalid implicit conversion from " +
+ type(*(*arguments)[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+ }
}
void TypeChecker::endVisit(UsingForDirective const& _usingFor)
@@ -731,7 +812,8 @@ void TypeChecker::visitManually(
vector<ContractDefinition const*> const& _bases
)
{
- std::vector<ASTPointer<Expression>> const& arguments = _modifier.arguments();
+ std::vector<ASTPointer<Expression>> const& arguments =
+ _modifier.arguments() ? *_modifier.arguments() : std::vector<ASTPointer<Expression>>();
for (ASTPointer<Expression> const& argument: arguments)
argument->accept(*this);
_modifier.name()->accept(*this);
@@ -769,7 +851,7 @@ void TypeChecker::visitManually(
);
return;
}
- for (size_t i = 0; i < _modifier.arguments().size(); ++i)
+ for (size_t i = 0; i < arguments.size(); ++i)
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])))
m_errorReporter.typeError(
arguments[i]->location(),
@@ -1551,16 +1633,22 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
_functionCall.expression().annotation().isPure &&
functionType->isPure();
+ bool allowDynamicTypes = m_evmVersion.supportsReturndata();
if (!functionType)
{
m_errorReporter.typeError(_functionCall.location(), "Type is not callable");
_functionCall.annotation().type = make_shared<TupleType>();
return false;
}
- else if (functionType->returnParameterTypes().size() == 1)
- _functionCall.annotation().type = functionType->returnParameterTypes().front();
+
+ auto returnTypes =
+ allowDynamicTypes ?
+ functionType->returnParameterTypes() :
+ functionType->returnParameterTypesWithoutDynamicTypes();
+ if (returnTypes.size() == 1)
+ _functionCall.annotation().type = returnTypes.front();
else
- _functionCall.annotation().type = make_shared<TupleType>(functionType->returnParameterTypes());
+ _functionCall.annotation().type = make_shared<TupleType>(returnTypes);
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
@@ -1600,7 +1688,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
- if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
+ if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
+ {
+ solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
+ m_errorReporter.typeError(
+ _functionCall.location(),
+ "Need at least " +
+ toString(parameterTypes.size()) +
+ " arguments for function call, but provided only " +
+ toString(arguments.size()) +
+ "."
+ );
+ }
+ else if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
{
bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
@@ -1623,15 +1723,36 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
else if (isPositionalCall)
{
- // call by positional arguments
+ bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
+
for (size_t i = 0; i < arguments.size(); ++i)
{
auto const& argType = type(*arguments[i]);
- if (functionType->takesArbitraryParameters())
+ if (functionType->takesArbitraryParameters() && i >= parameterTypes.size())
{
+ bool errored = false;
if (auto t = dynamic_cast<RationalNumberType const*>(argType.get()))
if (!t->mobileType())
+ {
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
+ errored = true;
+ }
+ if (!errored)
+ {
+ TypePointer encodingType;
+ if (
+ argType->mobileType() &&
+ argType->mobileType()->interfaceType(false) &&
+ argType->mobileType()->interfaceType(false)->encodingType()
+ )
+ encodingType = argType->mobileType()->interfaceType(false)->encodingType();
+ // Structs are fine as long as ABIV2 is activated and we do not do packed encoding.
+ if (!encodingType || (
+ dynamic_cast<StructType const*>(encodingType.get()) &&
+ !(abiEncodeV2 && functionType->padArguments())
+ ))
+ m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
+ }
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
m_errorReporter.typeError(
@@ -1867,7 +1988,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
m_errorReporter.warning(
_memberAccess.location(),
"Using contract member \"" + memberName +"\" inherited from the address type is deprecated." +
- " Convert the contract to \"address\" type to access the member."
+ " Convert the contract to \"address\" type to access the member,"
+ " for example use \"address(contract)." + memberName + "\" instead."
);
}
@@ -2025,10 +2147,9 @@ bool TypeChecker::visit(Identifier const& _identifier)
for (Declaration const* declaration: annotation.overloadedDeclarations)
{
- TypePointer function = declaration->type();
- solAssert(!!function, "Requested type not present.");
- auto const* functionType = dynamic_cast<FunctionType const*>(function.get());
- if (functionType && functionType->canTakeArguments(*annotation.argumentTypes))
+ FunctionTypePointer functionType = declaration->functionType(true);
+ solAssert(!!functionType, "Requested type not present.");
+ if (functionType->canTakeArguments(*annotation.argumentTypes))
candidates.push_back(declaration);
}
if (candidates.empty())
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 2ba31232..2245abd6 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -73,7 +73,12 @@ private:
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
void checkContractAbstractFunctions(ContractDefinition const& _contract);
- void checkContractAbstractConstructors(ContractDefinition const& _contract);
+ void checkContractBaseConstructorArguments(ContractDefinition const& _contract);
+ void annotateBaseConstructorArguments(
+ ContractDefinition const& _currentContract,
+ FunctionDefinition const* _baseConstructor,
+ ASTNode const* _argumentNode
+ );
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 13c3ab68..d9843012 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -305,10 +305,15 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
mutability = StateMutability::View;
break;
case Type::Category::Magic:
+ {
// we can ignore the kind of magic and only look at the name of the member
- if (member != "data" && member != "sig" && member != "blockhash")
+ set<string> static const pureMembers{
+ "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash"
+ };
+ if (!pureMembers.count(member))
mutability = StateMutability::View;
break;
+ }
case Type::Category::Struct:
{
if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage))
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 27220b1f..80f5d642 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -290,7 +290,14 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const
return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
}
-shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const
+ContractDefinition::ContractKind FunctionDefinition::inContractKind() const
+{
+ auto contractDef = dynamic_cast<ContractDefinition const*>(scope());
+ solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set.");
+ return contractDef->contractKind();
+}
+
+FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
{
if (_internal)
{
@@ -331,6 +338,7 @@ shared_ptr<FunctionType> FunctionDefinition::functionType(bool _internal) const
TypePointer FunctionDefinition::type() const
{
+ solAssert(visibility() != Declaration::Visibility::External, "");
return make_shared<FunctionType>(*this);
}
@@ -372,7 +380,7 @@ TypePointer EventDefinition::type() const
return make_shared<FunctionType>(*this);
}
-std::shared_ptr<FunctionType> EventDefinition::functionType(bool _internal) const
+FunctionTypePointer EventDefinition::functionType(bool _internal) const
{
if (_internal)
return make_shared<FunctionType>(*this);
@@ -477,7 +485,7 @@ TypePointer VariableDeclaration::type() const
return annotation().type;
}
-shared_ptr<FunctionType> VariableDeclaration::functionType(bool _internal) const
+FunctionTypePointer VariableDeclaration::functionType(bool _internal) const
{
if (_internal)
return {};
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 863ad2fe..a53987bf 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -203,6 +203,7 @@ public:
bool isPublic() const { return visibility() >= Visibility::Public; }
virtual bool isVisibleInContract() const { return visibility() != Visibility::External; }
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
+ bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; }
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
@@ -217,7 +218,7 @@ public:
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
/// @returns null when it is not accessible as a function.
- virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const { return {}; }
+ virtual FunctionTypePointer functionType(bool /*_internal*/) const { return {}; }
protected:
virtual Visibility defaultVisibility() const { return Visibility::Public; }
@@ -424,19 +425,22 @@ public:
InheritanceSpecifier(
SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _baseName,
- std::vector<ASTPointer<Expression>> _arguments
+ std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
- ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
+ ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
UserDefinedTypeName const& name() const { return *m_baseName; }
- std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
+ // Returns nullptr if no argument list was given (``C``).
+ // If an argument list is given (``C(...)``), the arguments are returned
+ // as a vector of expressions. Note that this vector can be empty (``C()``).
+ std::vector<ASTPointer<Expression>> const* arguments() const { return m_arguments.get(); }
private:
ASTPointer<UserDefinedTypeName> m_baseName;
- std::vector<ASTPointer<Expression>> m_arguments;
+ std::unique_ptr<std::vector<ASTPointer<Expression>>> m_arguments;
};
/**
@@ -606,7 +610,8 @@ public:
StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; }
- bool isFallback() const { return name().empty(); }
+ bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); }
+ bool isFallback() const { return !m_isConstructor && name().empty(); }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
@@ -623,11 +628,13 @@ public:
/// arguments separated by commas all enclosed in parentheses without any spaces.
std::string externalSignature() const;
+ ContractDefinition::ContractKind inContractKind() const;
+
virtual TypePointer type() const override;
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
/// @returns null when it is not accessible as a function.
- virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override;
+ virtual FunctionTypePointer functionType(bool /*_internal*/) const override;
virtual FunctionDefinitionAnnotation& annotation() const override;
@@ -696,7 +703,7 @@ public:
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
/// @returns null when it is not accessible as a function.
- virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override;
+ virtual FunctionTypePointer functionType(bool /*_internal*/) const override;
virtual VariableDeclarationAnnotation& annotation() const override;
@@ -755,19 +762,22 @@ public:
ModifierInvocation(
SourceLocation const& _location,
ASTPointer<Identifier> const& _name,
- std::vector<ASTPointer<Expression>> _arguments
+ std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
- ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
+ ASTNode(_location), m_modifierName(_name), m_arguments(std::move(_arguments)) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ASTPointer<Identifier> const& name() const { return m_modifierName; }
- std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
+ // Returns nullptr if no argument list was given (``mod``).
+ // If an argument list is given (``mod(...)``), the arguments are returned
+ // as a vector of expressions. Note that this vector can be empty (``mod()``).
+ std::vector<ASTPointer<Expression>> const* arguments() const { return m_arguments.get(); }
private:
ASTPointer<Identifier> m_modifierName;
- std::vector<ASTPointer<Expression>> m_arguments;
+ std::unique_ptr<std::vector<ASTPointer<Expression>>> m_arguments;
};
/**
@@ -795,7 +805,7 @@ public:
bool isAnonymous() const { return m_anonymous; }
virtual TypePointer type() const override;
- virtual std::shared_ptr<FunctionType> functionType(bool /*_internal*/) const override;
+ virtual FunctionTypePointer functionType(bool /*_internal*/) const override;
virtual EventDefinitionAnnotation& annotation() const override;
@@ -821,6 +831,11 @@ public:
solAssert(false, "MagicVariableDeclaration used inside real AST.");
}
+ virtual FunctionTypePointer functionType(bool) const override
+ {
+ solAssert(m_type->category() == Type::Category::Function, "");
+ return std::dynamic_pointer_cast<FunctionType const>(m_type);
+ }
virtual TypePointer type() const override { return m_type; }
private:
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 3d4236cc..5cbe42bd 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -90,6 +90,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota
/// List of contracts this contract creates, i.e. which need to be compiled first.
/// Also includes all contracts from @a linearizedBaseContracts.
std::set<ContractDefinition const*> contractDependencies;
+ /// Mapping containing the nodes that define the arguments for base constructors.
+ /// These can either be inheritance specifiers or modifier invocations.
+ std::map<FunctionDefinition const*, ASTNode const*> baseConstructorArguments;
};
struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 4fef67c3..b8e00b60 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -134,10 +134,10 @@ string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePat
return boost::algorithm::join(_namePath, ".");
}
-Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp)
+Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short)
{
Json::Value typeDescriptions(Json::objectValue);
- typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString()) : Json::nullValue;
+ typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString(_short)) : Json::nullValue;
typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue;
return typeDescriptions;
@@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
{
setJsonNode(_node, "InheritanceSpecifier", {
make_pair("baseName", toJson(_node.name())),
- make_pair("arguments", toJson(_node.arguments()))
+ make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue)
});
return false;
}
@@ -354,7 +354,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue),
make_pair("scope", idOrNull(_node.scope())),
- make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
};
if (m_inEvent)
attributes.push_back(make_pair("indexed", _node.isIndexed()));
@@ -378,7 +378,7 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node)
{
setJsonNode(_node, "ModifierInvocation", {
make_pair("modifierName", toJson(*_node.name())),
- make_pair("arguments", toJson(_node.arguments()))
+ make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue)
});
return false;
}
@@ -399,7 +399,7 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
setJsonNode(_node, "ElementaryTypeName", {
make_pair("name", _node.typeName().toString()),
- make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
});
return false;
}
@@ -410,7 +410,7 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
make_pair("name", namePathToString(_node.namePath())),
make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
make_pair("contractScope", idOrNull(_node.annotation().contractScope)),
- make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
});
return false;
}
@@ -425,7 +425,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
- make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
});
return false;
}
@@ -435,7 +435,7 @@ bool ASTJsonConverter::visit(Mapping const& _node)
setJsonNode(_node, "Mapping", {
make_pair("keyType", toJson(_node.keyType())),
make_pair("valueType", toJson(_node.valueType())),
- make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
});
return false;
}
@@ -445,7 +445,7 @@ bool ASTJsonConverter::visit(ArrayTypeName const& _node)
setJsonNode(_node, "ArrayTypeName", {
make_pair("baseType", toJson(_node.baseType())),
make_pair("length", toJsonOrNull(_node.length())),
- make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
+ make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
});
return false;
}
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index 88b93699..29712f3b 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -152,7 +152,7 @@ private:
}
return tmp;
}
- static Json::Value typePointerToJson(TypePointer _tp);
+ static Json::Value typePointerToJson(TypePointer _tp, bool _short = false);
static Json::Value typePointerToJson(std::shared_ptr<std::vector<TypePointer>> _tps);
void appendExpressionAttributes(
std::vector<std::pair<std::string, Json::Value>> &_attributes,
diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h
index 70ee997e..aeff6e4a 100644
--- a/libsolidity/ast/AST_accept.h
+++ b/libsolidity/ast/AST_accept.h
@@ -94,7 +94,8 @@ void InheritanceSpecifier::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
- listAccept(m_arguments, _visitor);
+ if (m_arguments)
+ listAccept(*m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
@@ -104,7 +105,8 @@ void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
- listAccept(m_arguments, _visitor);
+ if (m_arguments)
+ listAccept(*m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
@@ -262,7 +264,8 @@ void ModifierInvocation::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this))
{
m_modifierName->accept(_visitor);
- listAccept(m_arguments, _visitor);
+ if (m_arguments)
+ listAccept(*m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
@@ -272,7 +275,8 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this))
{
m_modifierName->accept(_visitor);
- listAccept(m_arguments, _visitor);
+ if (m_arguments)
+ listAccept(*m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index c08e0e67..51739cb0 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -28,6 +28,7 @@
#include <libdevcore/CommonData.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/UTF8.h>
+#include <libdevcore/Algorithms.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -43,6 +44,85 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
+namespace
+{
+
+unsigned int mostSignificantBit(bigint const& _number)
+{
+#if BOOST_VERSION < 105500
+ solAssert(_number > 0, "");
+ bigint number = _number;
+ unsigned int result = 0;
+ while (number != 0)
+ {
+ number >>= 1;
+ ++result;
+ }
+ return --result;
+#else
+ return boost::multiprecision::msb(_number);
+#endif
+}
+
+/// Check whether (_base ** _exp) fits into 4096 bits.
+bool fitsPrecisionExp(bigint const& _base, bigint const& _exp)
+{
+ if (_base == 0)
+ return true;
+
+ solAssert(_base > 0, "");
+
+ size_t const bitsMax = 4096;
+
+ unsigned mostSignificantBaseBit = mostSignificantBit(_base);
+ if (mostSignificantBaseBit == 0) // _base == 1
+ return true;
+ if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096
+ return false;
+
+ bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1);
+
+ return bitsNeeded <= bitsMax;
+}
+
+/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits,
+/// where X is given indirectly via _log2OfBase = log2(X).
+bool fitsPrecisionBaseX(
+ bigint const& _mantissa,
+ double _log2OfBase,
+ uint32_t _exp
+)
+{
+ if (_mantissa == 0)
+ return true;
+
+ solAssert(_mantissa > 0, "");
+
+ size_t const bitsMax = 4096;
+
+ unsigned mostSignificantMantissaBit = mostSignificantBit(_mantissa);
+ if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096
+ return false;
+
+ bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1;
+ return bitsNeeded <= bitsMax;
+}
+
+/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits.
+bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10)
+{
+ double const log2Of10AwayFromZero = 3.3219280948873624;
+ return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10);
+}
+
+/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits.
+bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2)
+{
+ return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
+}
+
+}
+
void StorageOffsets::computeOffsets(TypePointers const& _types)
{
bigint slotOffset = 0;
@@ -208,9 +288,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
case Token::UInt:
return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned);
case Token::Fixed:
- return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed);
+ return make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed);
case Token::UFixed:
- return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned);
+ return make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned);
case Token::Byte:
return make_shared<FixedBytesType>(1);
case Token::Address:
@@ -232,11 +312,22 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
TypePointer Type::fromElementaryTypeName(string const& _name)
{
+ string name = _name;
+ DataLocation location = DataLocation::Storage;
+ if (boost::algorithm::ends_with(name, " memory"))
+ {
+ name = name.substr(0, name.length() - 7);
+ location = DataLocation::Memory;
+ }
unsigned short firstNum;
unsigned short secondNum;
Token::Value token;
- tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(_name);
- return fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum));
+ tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(name);
+ auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum));
+ if (auto* ref = dynamic_cast<ReferenceType const*>(t.get()))
+ return ref->copyForLocation(location, true);
+ else
+ return t;
}
TypePointer Type::forLiteral(Literal const& _literal)
@@ -304,7 +395,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
);
for (FunctionDefinition const* function: library.definedFunctions())
{
- if (!function->isVisibleInDerivedContracts() || seenFunctions.count(function))
+ if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function))
continue;
seenFunctions.insert(function);
FunctionType funType(*function, false);
@@ -327,7 +418,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT
else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType))
return !otherInt->isAddress();
else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType))
- return otherRat->integerType() && !otherRat->integerType()->isSigned();
+ return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned();
else
return false;
}
@@ -677,31 +768,39 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
}
else if (expPoint != _literal.value().end())
{
- // parse the exponent
+ // Parse base and exponent. Checks numeric limit.
bigint exp = bigint(string(expPoint + 1, _literal.value().end()));
if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
return make_tuple(false, rational(0));
- // parse the base
+ uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
+
+
tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint));
+
if (!get<0>(base))
return make_tuple(false, rational(0));
value = get<1>(base);
if (exp < 0)
{
- exp *= -1;
+ if (!fitsPrecisionBase10(abs(value.denominator()), expAbs))
+ return make_tuple(false, rational(0));
value /= boost::multiprecision::pow(
bigint(10),
- exp.convert_to<int32_t>()
+ expAbs
);
}
- else
+ else if (exp > 0)
+ {
+ if (!fitsPrecisionBase10(abs(value.numerator()), expAbs))
+ return make_tuple(false, rational(0));
value *= boost::multiprecision::pow(
bigint(10),
- exp.convert_to<int32_t>()
+ expAbs
);
+ }
}
else
{
@@ -827,10 +926,10 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
{
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{
- auto mobile = mobileType();
- if (!mobile)
+ auto commonType = Type::commonType(shared_from_this(), _other);
+ if (!commonType)
return TypePointer();
- return mobile->binaryOperatorResult(_operator, _other);
+ return commonType->binaryOperatorResult(_operator, _other);
}
else if (_other->category() != category())
return TypePointer();
@@ -900,16 +999,49 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
using boost::multiprecision::pow;
if (other.isFractional())
return TypePointer();
- else if (abs(other.m_value) > numeric_limits<uint32_t>::max())
- return TypePointer(); // This will need too much memory to represent.
- uint32_t exponent = abs(other.m_value).numerator().convert_to<uint32_t>();
- bigint numerator = pow(m_value.numerator(), exponent);
- bigint denominator = pow(m_value.denominator(), exponent);
- if (other.m_value >= 0)
- value = rational(numerator, denominator);
+ solAssert(other.m_value.denominator() == 1, "");
+ bigint const& exp = other.m_value.numerator();
+
+ // x ** 0 = 1
+ // for 0, 1 and -1 the size of the exponent doesn't have to be restricted
+ if (exp == 0)
+ value = 1;
+ else if (m_value.numerator() == 0 || m_value == 1)
+ value = m_value;
+ else if (m_value == -1)
+ {
+ bigint isOdd = abs(exp) & bigint(1);
+ value = 1 - 2 * isOdd.convert_to<int>();
+ }
else
- // invert
- value = rational(denominator, numerator);
+ {
+ if (abs(exp) > numeric_limits<uint32_t>::max())
+ return TypePointer(); // This will need too much memory to represent.
+
+ uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
+
+ // Limit size to 4096 bits
+ if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp))
+ return TypePointer();
+
+ static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
+ if (_base == 1)
+ return 1;
+ else if (_base == -1)
+ return 1 - 2 * int(_exponent & 1);
+ else
+ return pow(_base, _exponent);
+ };
+
+ bigint numerator = optimizedPow(m_value.numerator(), absExp);
+ bigint denominator = optimizedPow(m_value.denominator(), absExp);
+
+ if (exp >= 0)
+ value = rational(numerator, denominator);
+ else
+ // invert
+ value = rational(denominator, numerator);
+ }
break;
}
case Token::SHL:
@@ -921,28 +1053,48 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
return TypePointer();
else if (other.m_value > numeric_limits<uint32_t>::max())
return TypePointer();
- uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
- value = m_value.numerator() * pow(bigint(2), exponent);
+ if (m_value.numerator() == 0)
+ value = 0;
+ else
+ {
+ uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
+ if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent))
+ return TypePointer();
+ value = m_value.numerator() * pow(bigint(2), exponent);
+ }
break;
}
// NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue
// determines the resulting type and the type of shift (SAR or SHR).
case Token::SAR:
{
- using boost::multiprecision::pow;
+ namespace mp = boost::multiprecision;
if (fractional)
return TypePointer();
else if (other.m_value < 0)
return TypePointer();
else if (other.m_value > numeric_limits<uint32_t>::max())
return TypePointer();
- uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
- value = rational(m_value.numerator() / pow(bigint(2), exponent), 1);
+ if (m_value.numerator() == 0)
+ value = 0;
+ else
+ {
+ uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
+ if (exponent > mostSignificantBit(mp::abs(m_value.numerator())))
+ value = 0;
+ else
+ value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1);
+ }
break;
}
default:
return TypePointer();
}
+
+ // verify that numerator and denominator fit into 4096 bit after every operation
+ if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096)
+ return TypePointer();
+
return make_shared<RationalNumberType>(value);
}
}
@@ -1262,6 +1414,8 @@ bool ContractType::isPayable() const
TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
{
+ if (isSuper())
+ return TypePointer{};
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
@@ -1969,25 +2123,19 @@ bool StructType::recursive() const
{
if (!m_recursive.is_initialized())
{
- set<StructDefinition const*> structsSeen;
- function<bool(StructType const*)> check = [&](StructType const* t) -> bool
+ auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector)
{
- StructDefinition const* str = &t->structDefinition();
- if (structsSeen.count(str))
- return true;
- structsSeen.insert(str);
- for (ASTPointer<VariableDeclaration> const& variable: str->members())
+ for (ASTPointer<VariableDeclaration> const& variable: _struct.members())
{
Type const* memberType = variable->annotation().type.get();
while (dynamic_cast<ArrayType const*>(memberType))
memberType = dynamic_cast<ArrayType const*>(memberType)->baseType().get();
if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
- if (check(innerStruct))
- return true;
+ if (_cycleDetector.run(innerStruct->structDefinition()))
+ return;
}
- return false;
};
- m_recursive = check(this);
+ m_recursive = (CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr);
}
return *m_recursive;
}
@@ -2311,6 +2459,18 @@ vector<string> FunctionType::parameterNames() const
return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend());
}
+TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const
+{
+ TypePointers returnParameterTypes = m_returnParameterTypes;
+
+ if (m_kind == Kind::External || m_kind == Kind::CallCode || m_kind == Kind::DelegateCall)
+ for (auto& param: returnParameterTypes)
+ if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage))
+ param = make_shared<InaccessibleDynamicType>();
+
+ return returnParameterTypes;
+}
+
TypePointers FunctionType::parameterTypes() const
{
if (!bound())
@@ -2355,7 +2515,11 @@ string FunctionType::richIdentifier() const
case Kind::ByteArrayPush: id += "bytearraypush"; break;
case Kind::ObjectCreation: id += "objectcreation"; break;
case Kind::Assert: id += "assert"; break;
- case Kind::Require: id += "require";break;
+ case Kind::Require: id += "require"; break;
+ case Kind::ABIEncode: id += "abiencode"; break;
+ case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
+ case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
+ case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
default: solAssert(false, "Unknown function location."); break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
@@ -2772,18 +2936,9 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
kind = Kind::DelegateCall;
}
- TypePointers returnParameterTypes = m_returnParameterTypes;
- if (kind != Kind::Internal)
- {
- // Alter dynamic types to be non-accessible.
- for (auto& param: returnParameterTypes)
- if (param->isDynamicallySized())
- param = make_shared<InaccessibleDynamicType>();
- }
-
return make_shared<FunctionType>(
parameterTypes,
- returnParameterTypes,
+ m_returnParameterTypes,
m_parameterNames,
m_returnParameterNames,
kind,
@@ -2875,7 +3030,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
}
if (contract.isLibrary())
for (FunctionDefinition const* function: contract.definedFunctions())
- if (function->isVisibleInDerivedContracts())
+ if (function->isVisibleAsLibraryMember())
members.push_back(MemberList::Member(
function->name(),
FunctionType(*function).asMemberFunction(true),
@@ -2985,6 +3140,8 @@ string MagicType::richIdentifier() const
return "t_magic_message";
case Kind::Transaction:
return "t_magic_transaction";
+ case Kind::ABI:
+ return "t_magic_abi";
default:
solAssert(false, "Unknown kind of magic");
}
@@ -3025,6 +3182,45 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{"origin", make_shared<IntegerType>(160, IntegerType::Modifier::Address)},
{"gasprice", make_shared<IntegerType>(256)}
});
+ case Kind::ABI:
+ return MemberList::MemberMap({
+ {"encode", make_shared<FunctionType>(
+ TypePointers(),
+ TypePointers{make_shared<ArrayType>(DataLocation::Memory)},
+ strings{},
+ strings{},
+ FunctionType::Kind::ABIEncode,
+ true,
+ StateMutability::Pure
+ )},
+ {"encodePacked", make_shared<FunctionType>(
+ TypePointers(),
+ TypePointers{make_shared<ArrayType>(DataLocation::Memory)},
+ strings{},
+ strings{},
+ FunctionType::Kind::ABIEncodePacked,
+ true,
+ StateMutability::Pure
+ )},
+ {"encodeWithSelector", make_shared<FunctionType>(
+ TypePointers{make_shared<FixedBytesType>(4)},
+ TypePointers{make_shared<ArrayType>(DataLocation::Memory)},
+ strings{},
+ strings{},
+ FunctionType::Kind::ABIEncodeWithSelector,
+ true,
+ StateMutability::Pure
+ )},
+ {"encodeWithSignature", make_shared<FunctionType>(
+ TypePointers{make_shared<ArrayType>(DataLocation::Memory, true)},
+ TypePointers{make_shared<ArrayType>(DataLocation::Memory)},
+ strings{},
+ strings{},
+ FunctionType::Kind::ABIEncodeWithSignature,
+ true,
+ StateMutability::Pure
+ )}
+ });
default:
solAssert(false, "Unknown kind of magic.");
}
@@ -3040,6 +3236,8 @@ string MagicType::toString(bool) const
return "msg";
case Kind::Transaction:
return "tx";
+ case Kind::ABI:
+ return "abi";
default:
solAssert(false, "Unknown kind of magic.");
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index c20a025f..345f84a1 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -150,6 +150,7 @@ public:
/// @name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static TypePointer fromElementaryTypeName(ElementaryTypeNameToken const& _type);
+ /// Converts a given elementary type name with optional suffix " memory" to a type pointer.
static TypePointer fromElementaryTypeName(std::string const& _name);
/// @}
@@ -229,6 +230,9 @@ public:
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned sizeOnStack() const { return 1; }
+ /// If it is possible to initialize such a value in memory by just writing zeros
+ /// of the size memoryHeadSize().
+ virtual bool hasSimpleZeroValueInMemory() const { return true; }
/// @returns the mobile (in contrast to static) type corresponding to the given type.
/// This returns the corresponding IntegerType or FixedPointType for RationalNumberType
/// and the pointer type for storage reference types.
@@ -399,7 +403,7 @@ private:
};
/**
- * Integer and fixed point constants either literals or computed.
+ * Integer and fixed point constants either literals or computed.
* Example expressions: 2, 3.14, 2+10.2, ~10.
* There is one distinct type per value.
*/
@@ -411,7 +415,7 @@ public:
/// @returns true if the literal is a valid integer.
static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
-
+
explicit RationalNumberType(rational const& _value):
m_value(_value)
{}
@@ -432,7 +436,7 @@ public:
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> integerType() const;
- /// @returns the smallest fixed type that can hold the value or incurs the least precision loss.
+ /// @returns the smallest fixed type that can hold the value or incurs the least precision loss.
/// If the integer part does not fit, returns an empty pointer.
std::shared_ptr<FixedPointType const> fixedPointType() const;
@@ -442,6 +446,9 @@ public:
/// @returns true if the value is negative.
bool isNegative() const { return m_value < 0; }
+ /// @returns true if the value is zero.
+ bool isZero() const { return m_value == 0; }
+
private:
rational m_value;
@@ -568,6 +575,7 @@ public:
virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); }
virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; }
+ virtual bool hasSimpleZeroValueInMemory() const override { return false; }
/// Storage references can be pointers or bound references. In general, local variables are of
/// pointer type, state variables are bound references. Assignments to pointers or deleting
@@ -692,22 +700,27 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded ) const override
{
+ solAssert(!isSuper(), "");
return encodingType()->calldataEncodedSize(_padded);
}
- virtual unsigned storageBytes() const override { return 20; }
- virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
+ virtual bool canLiveOutsideStorage() const override { return !isSuper(); }
virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; }
- virtual bool isValueType() const override { return true; }
+ virtual bool isValueType() const override { return !isSuper(); }
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName() const override;
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override
{
+ if (isSuper())
+ return TypePointer{};
return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
}
virtual TypePointer interfaceType(bool _inLibrary) const override
{
+ if (isSuper())
+ return TypePointer{};
return _inLibrary ? shared_from_this() : encodingType();
}
@@ -768,7 +781,7 @@ public:
virtual std::string canonicalName() const override;
virtual std::string signatureInExternalFunction(bool _structsByName) const override;
- /// @returns a function that peforms the type conversion between a list of struct members
+ /// @returns a function that performs the type conversion between a list of struct members
/// and a memory struct of this type.
FunctionTypePointer constructorType() const;
@@ -850,6 +863,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
+ virtual bool hasSimpleZeroValueInMemory() const override { return false; }
virtual TypePointer mobileType() const override;
/// Converts components to their temporary types and performs some wildcard matching.
virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override;
@@ -903,6 +917,10 @@ public:
ObjectCreation, ///< array creation using new
Assert, ///< assert()
Require, ///< require()
+ ABIEncode,
+ ABIEncodePacked,
+ ABIEncodeWithSelector,
+ ABIEncodeWithSignature,
GasLeft ///< gasleft()
};
@@ -973,6 +991,9 @@ public:
TypePointers parameterTypes() const;
std::vector<std::string> parameterNames() const;
TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; }
+ /// @returns the list of return parameter types. All dynamically-sized types (this excludes
+ /// storage pointers) are replaced by InaccessibleDynamicType instances.
+ TypePointers returnParameterTypesWithoutDynamicTypes() const;
std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; }
/// @returns the "self" parameter type for a bound function
TypePointer const& selfType() const;
@@ -991,6 +1012,7 @@ public:
virtual bool isValueType() const override { return true; }
virtual bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
virtual unsigned sizeOnStack() const override;
+ virtual bool hasSimpleZeroValueInMemory() const override { return false; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override;
virtual TypePointer interfaceType(bool _inLibrary) const override;
@@ -1024,7 +1046,7 @@ public:
return *m_declaration;
}
bool hasDeclaration() const { return !!m_declaration; }
- /// @returns true if the the result of this function only depends on its arguments
+ /// @returns true if the result of this function only depends on its arguments
/// and it does not modify the state.
/// Currently, this will only return true for internal functions like keccak and ecrecover.
bool isPure() const;
@@ -1034,14 +1056,14 @@ public:
ASTPointer<ASTString> documentation() const;
/// 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); }
+ 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; }
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
bool bound() const { return m_bound; }
/// @returns a copy of this type, where gas or value are set manually. This will never set one
- /// of the parameters to fals.
+ /// of the parameters to false.
TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const;
/// @returns a copy of this function type where all return parameters of dynamic size are
@@ -1096,6 +1118,8 @@ public:
return _inLibrary ? shared_from_this() : TypePointer();
}
virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; }
+ /// Cannot be stored in memory, but just in case.
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
TypePointer const& keyType() const { return m_keyType; }
TypePointer const& valueType() const { return m_valueType; }
@@ -1124,6 +1148,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
@@ -1146,6 +1171,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override { return 0; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override;
@@ -1171,6 +1197,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual unsigned sizeOnStack() const override { return 0; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
@@ -1187,7 +1214,7 @@ private:
class MagicType: public Type
{
public:
- enum class Kind { Block, Message, Transaction };
+ enum class Kind { Block, Message, Transaction, ABI };
virtual Category category() const override { return Category::Magic; }
explicit MagicType(Kind _kind): m_kind(_kind) {}
@@ -1201,6 +1228,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual unsigned sizeOnStack() const override { return 0; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
@@ -1230,6 +1258,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; }
virtual bool isValueType() const override { return true; }
virtual unsigned sizeOnStack() const override { return 1; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual std::string toString(bool) const override { return "inaccessible dynamic type"; }
virtual TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); }
};
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 00f59065..8e890854 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -253,6 +253,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
templ("body", w.render());
break;
}
+ case Type::Category::InaccessibleDynamic:
+ templ("body", "cleaned := 0");
+ break;
default:
solAssert(false, "Cleanup of type " + _type.identifier() + " requested.");
}
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index ce8cbb5f..0fe66d2d 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -741,10 +741,10 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
if (_type.isByteArray())
// For a "long" byte array, store length as 2*length+1
_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
- _context<< Instruction::DUP4 << Instruction::SSTORE;
+ _context << Instruction::DUP4 << Instruction::SSTORE;
// skip if size is not reduced
_context << Instruction::DUP2 << Instruction::DUP2
- << Instruction::ISZERO << Instruction::GT;
+ << Instruction::GT << Instruction::ISZERO;
_context.appendConditionalJumpTo(resizeEnd);
// size reduced, clear the end of the array
@@ -774,6 +774,55 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
);
}
+void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const
+{
+ solAssert(_type.location() == DataLocation::Storage, "");
+ solAssert(_type.isDynamicallySized(), "");
+ if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
+ solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
+
+ if (_type.isByteArray())
+ {
+ // We almost always just add 2 (length of byte arrays is shifted left by one)
+ // except for the case where we transition from a short byte array
+ // to a long byte array, there we have to copy.
+ // This happens if the length is exactly 31, which means that the
+ // lowest-order byte (we actually use a mask with fewer bits) must
+ // be (31*2+0) = 62
+
+ m_context.appendInlineAssembly(R"({
+ let data := sload(ref)
+ let shifted_length := and(data, 63)
+ // We have to copy if length is exactly 31, because that marks
+ // the transition between in-place and out-of-place storage.
+ switch shifted_length
+ case 62
+ {
+ mstore(0, ref)
+ let data_area := keccak256(0, 0x20)
+ sstore(data_area, and(data, not(0xff)))
+ // New length is 32, encoded as (32 * 2 + 1)
+ sstore(ref, 65)
+ // Replace ref variable by new length
+ ref := 32
+ }
+ default
+ {
+ sstore(ref, add(data, 2))
+ // Replace ref variable by new length
+ if iszero(and(data, 1)) { data := shifted_length }
+ ref := add(div(data, 2), 1)
+ }
+ })", {"ref"});
+ }
+ else
+ m_context.appendInlineAssembly(R"({
+ let new_length := add(sload(ref), 1)
+ sstore(ref, new_length)
+ ref := new_length
+ })", {"ref"});
+}
+
void ArrayUtils::clearStorageLoop(TypePointer const& _type) const
{
m_context.callLowLevelFunction(
diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h
index f3ddc4ee..99786397 100644
--- a/libsolidity/codegen/ArrayUtils.h
+++ b/libsolidity/codegen/ArrayUtils.h
@@ -67,6 +67,12 @@ public:
/// Stack pre: reference (excludes byte offset) new_length
/// Stack post:
void resizeDynamicArray(ArrayType const& _type) const;
+ /// Increments the size of a dynamic array by one.
+ /// Does not touch the new data element. In case of a byte array, this might move the
+ /// data.
+ /// Stack pre: reference (excludes byte offset)
+ /// Stack post: new_length
+ void incrementDynamicArraySize(ArrayType const& _type) const;
/// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
/// Stack pre: end_ref start_ref
/// Stack post: end_ref
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 0bf88267..a35eea73 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -193,14 +193,22 @@ Declaration const* CompilerContext::nextFunctionToCompile() const
return m_functionCompilationQueue.nextFunctionToCompile();
}
-ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const
+ModifierDefinition const& CompilerContext::resolveVirtualFunctionModifier(
+ ModifierDefinition const& _modifier
+) const
{
+ // Libraries do not allow inheritance and their functions can be inlined, so we should not
+ // search the inheritance hierarchy (which will be the wrong one in case the function
+ // is inlined).
+ if (auto scope = dynamic_cast<ContractDefinition const*>(_modifier.scope()))
+ if (scope->isLibrary())
+ return _modifier;
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
for (ContractDefinition const* contract: m_inheritanceHierarchy)
for (ModifierDefinition const* modifier: contract->functionModifiers())
- if (modifier->name() == _name)
+ if (modifier->name() == _modifier.name())
return *modifier;
- solAssert(false, "Function modifier " + _name + " not found.");
+ solAssert(false, "Function modifier " + _modifier.name() + " not found in inheritance hierarchy.");
}
unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const
@@ -254,12 +262,20 @@ CompilerContext& CompilerContext::appendRevert()
return *this << u256(0) << u256(0) << Instruction::REVERT;
}
-CompilerContext& CompilerContext::appendConditionalRevert()
+CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData)
{
- *this << Instruction::ISZERO;
- eth::AssemblyItem afterTag = appendConditionalJump();
- appendRevert();
- *this << afterTag;
+ if (_forwardReturnData && m_evmVersion.supportsReturndata())
+ appendInlineAssembly(R"({
+ if condition {
+ returndatacopy(0, 0, returndatasize())
+ revert(0, returndatasize())
+ }
+ })", {"condition"});
+ else
+ appendInlineAssembly(R"({
+ if condition { revert(0, 0) }
+ })", {"condition"});
+ *this << Instruction::POP;
return *this;
}
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index cf626683..098472f7 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -130,7 +130,7 @@ public:
void appendMissingLowLevelFunctions();
ABIFunctions& abiFunctions() { return m_abiFunctions; }
- ModifierDefinition const& functionModifier(std::string const& _name) const;
+ ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const;
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
/// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns
@@ -156,8 +156,11 @@ public:
CompilerContext& appendConditionalInvalid();
/// Appends a REVERT(0, 0) call
CompilerContext& appendRevert();
- /// Appends a conditional REVERT(0, 0) call
- CompilerContext& appendConditionalRevert();
+ /// Appends a conditional REVERT-call, either forwarding the RETURNDATA or providing the
+ /// empty string. Consumes the condition.
+ /// If the current EVM version does not support RETURNDATA, uses REVERT but does not forward
+ /// the data.
+ CompilerContext& appendConditionalRevert(bool _forwardReturnData = false);
/// Appends a JUMP to a specific tag
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
/// Appends pushing of a new tag and @returns the new tag.
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 533aca5c..4af7d905 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -21,12 +21,16 @@
*/
#include <libsolidity/codegen/CompilerUtils.h>
+
#include <libsolidity/ast/AST.h>
-#include <libevmasm/Instruction.h>
#include <libsolidity/codegen/ArrayUtils.h>
#include <libsolidity/codegen/LValue.h>
#include <libsolidity/codegen/ABIFunctions.h>
+#include <libevmasm/Instruction.h>
+
+#include <libdevcore/Whiskers.h>
+
using namespace std;
namespace dev
@@ -36,11 +40,17 @@ namespace solidity
const unsigned CompilerUtils::dataStartOffset = 4;
const size_t CompilerUtils::freeMemoryPointer = 64;
+const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32;
+const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32;
const unsigned CompilerUtils::identityContractAddress = 4;
+static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area.");
+static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer.");
+static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPointer + 32, "General purpose memory must not overlap with zero area.");
+
void CompilerUtils::initialiseFreeMemoryPointer()
{
- m_context << u256(freeMemoryPointer + 32);
+ m_context << u256(generalPurposeMemoryStart);
storeFreeMemoryPointer();
}
@@ -68,6 +78,20 @@ void CompilerUtils::toSizeAfterFreeMemoryPointer()
m_context << Instruction::SWAP1;
}
+void CompilerUtils::revertWithStringData(Type const& _argumentType)
+{
+ solAssert(_argumentType.isImplicitlyConvertibleTo(*Type::fromElementaryTypeName("string memory")), "");
+ fetchFreeMemoryPointer();
+ m_context << (u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256("Error(string)")))) << (256 - 32));
+ m_context << Instruction::DUP2 << Instruction::MSTORE;
+ m_context << u256(4) << Instruction::ADD;
+ // Stack: <string data> <mem pos of encoding start>
+ abiEncode({_argumentType.shared_from_this()}, {make_shared<ArrayType>(DataLocation::Memory, true)});
+ toSizeAfterFreeMemoryPointer();
+ m_context << Instruction::REVERT;
+ m_context.adjustStackOffset(_argumentType.sizeOnStack());
+}
+
unsigned CompilerUtils::loadFromMemory(
unsigned _offset,
Type const& _type,
@@ -139,7 +163,6 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
)
{
- solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
combineExternalFunctionType(true);
m_context << Instruction::DUP2 << Instruction::MSTORE;
m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD;
@@ -159,6 +182,163 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
}
}
+void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory, bool _revertOnOutOfBounds)
+{
+ /// Stack: <source_offset> <length>
+ if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
+ {
+ // Use the new JULIA-based decoding function
+ auto stackHeightBefore = m_context.stackHeight();
+ abiDecodeV2(_typeParameters, _fromMemory);
+ solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, "");
+ return;
+ }
+
+ //@todo this does not yet support nested dynamic arrays
+
+ if (_revertOnOutOfBounds)
+ {
+ size_t encodedSize = 0;
+ for (auto const& t: _typeParameters)
+ encodedSize += t->decodingType()->calldataEncodedSize(true);
+ m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"});
+ }
+
+ m_context << Instruction::DUP2 << Instruction::ADD;
+ m_context << Instruction::SWAP1;
+ /// Stack: <input_end> <source_offset>
+
+ // Retain the offset pointer as base_offset, the point from which the data offsets are computed.
+ m_context << Instruction::DUP1;
+ for (TypePointer const& parameterType: _typeParameters)
+ {
+ // stack: v1 v2 ... v(k-1) input_end base_offset current_offset
+ TypePointer type = parameterType->decodingType();
+ solUnimplementedAssert(type, "No decoding type found.");
+ if (type->category() == Type::Category::Array)
+ {
+ auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
+ solUnimplementedAssert(!arrayType.baseType()->isDynamicallyEncoded(), "Nested arrays not yet implemented.");
+ if (_fromMemory)
+ {
+ solUnimplementedAssert(
+ arrayType.baseType()->isValueType(),
+ "Nested memory arrays not yet implemented here."
+ );
+ // @todo If base type is an array or struct, it is still calldata-style encoded, so
+ // we would have to convert it like below.
+ solAssert(arrayType.location() == DataLocation::Memory, "");
+ if (arrayType.isDynamicallySized())
+ {
+ // compute data pointer
+ m_context << Instruction::DUP1 << Instruction::MLOAD;
+ if (_revertOnOutOfBounds)
+ {
+ // Check that the data pointer is valid and that length times
+ // item size is still inside the range.
+ Whiskers templ(R"({
+ if gt(ptr, 0x100000000) { revert(0, 0) }
+ ptr := add(ptr, base_offset)
+ let array_data_start := add(ptr, 0x20)
+ if gt(array_data_start, input_end) { revert(0, 0) }
+ let array_length := mload(ptr)
+ if or(
+ gt(array_length, 0x100000000),
+ gt(add(array_data_start, mul(array_length, <item_size>)), input_end)
+ ) { revert(0, 0) }
+ })");
+ templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true)));
+ m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"});
+ }
+ else
+ m_context << Instruction::DUP3 << Instruction::ADD;
+ // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k)
+ moveIntoStack(3);
+ m_context << u256(0x20) << Instruction::ADD;
+ }
+ else
+ {
+ // Size has already been checked for this one.
+ moveIntoStack(2);
+ m_context << Instruction::DUP3;
+ m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD;
+ }
+ }
+ else
+ {
+ // first load from calldata and potentially convert to memory if arrayType is memory
+ TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false);
+ if (calldataType->isDynamicallySized())
+ {
+ // put on stack: data_pointer length
+ loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
+ m_context << Instruction::SWAP1;
+ // stack: input_end base_offset next_pointer data_offset
+ if (_revertOnOutOfBounds)
+ m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"});
+ m_context << Instruction::DUP3 << Instruction::ADD;
+ // stack: input_end base_offset next_pointer array_head_ptr
+ if (_revertOnOutOfBounds)
+ m_context.appendInlineAssembly(
+ "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }",
+ {"input_end", "base_offset", "next_ptr", "array_head_ptr"}
+ );
+ // retrieve length
+ loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
+ // stack: input_end base_offset next_pointer array_length data_pointer
+ m_context << Instruction::SWAP2;
+ // stack: input_end base_offset data_pointer array_length next_pointer
+ if (_revertOnOutOfBounds)
+ {
+ unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true);
+ m_context.appendInlineAssembly(R"({
+ if or(
+ gt(array_length, 0x100000000),
+ gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end)
+ ) { revert(0, 0) }
+ })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"});
+ }
+ }
+ else
+ {
+ // size has already been checked
+ // stack: input_end base_offset data_offset
+ m_context << Instruction::DUP1;
+ m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD;
+ }
+ if (arrayType.location() == DataLocation::Memory)
+ {
+ // stack: input_end base_offset calldata_ref [length] next_calldata
+ // copy to memory
+ // move calldata type up again
+ moveIntoStack(calldataType->sizeOnStack());
+ convertType(*calldataType, arrayType, false, false, true);
+ // fetch next pointer again
+ moveToStackTop(arrayType.sizeOnStack());
+ }
+ // move input_end up
+ // stack: input_end base_offset calldata_ref [length] next_calldata
+ moveToStackTop(2 + arrayType.sizeOnStack());
+ m_context << Instruction::SWAP1;
+ // stack: base_offset calldata_ref [length] input_end next_calldata
+ moveToStackTop(2 + arrayType.sizeOnStack());
+ m_context << Instruction::SWAP1;
+ // stack: calldata_ref [length] input_end base_offset next_calldata
+ }
+ }
+ else
+ {
+ solAssert(!type->isDynamicallyEncoded(), "Unknown dynamically sized type: " + type->toString());
+ loadFromMemoryDynamic(*type, !_fromMemory, true);
+ // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset
+ moveToStackTop(1, type->sizeOnStack());
+ moveIntoStack(3, type->sizeOnStack());
+ }
+ // stack: v1 v2 ... v(k-1) v(k) input_end base_offset next_offset
+ }
+ popStackSlots(3);
+}
+
void CompilerUtils::encodeToMemory(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
@@ -321,15 +501,13 @@ void CompilerUtils::abiEncodeV2(
void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory)
{
- // stack: <source_offset>
+ // stack: <source_offset> <length> [stack top]
auto ret = m_context.pushNewTag();
+ moveIntoStack(2);
+ // stack: <return tag> <source_offset> <length> [stack top]
+ m_context << Instruction::DUP2 << Instruction::ADD;
m_context << Instruction::SWAP1;
- if (_fromMemory)
- // TODO pass correct size for the memory case
- m_context << (u256(1) << 63);
- else
- m_context << Instruction::CALLDATASIZE;
- m_context << Instruction::SWAP1;
+ // stack: <return tag> <end> <start>
string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory);
m_context.appendJumpTo(m_context.namedTag(decoderName));
m_context.adjustStackOffset(int(sizeOnStack(_parameterTypes)) - 3);
@@ -338,14 +516,34 @@ void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromM
void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
{
- auto repeat = m_context.newTag();
- m_context << repeat;
- pushZeroValue(*_type.baseType());
- storeInMemoryDynamic(*_type.baseType());
- m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1;
- m_context << Instruction::SUB << Instruction::SWAP1;
- m_context << Instruction::DUP2;
- m_context.appendConditionalJumpTo(repeat);
+ if (_type.baseType()->hasSimpleZeroValueInMemory())
+ {
+ solAssert(_type.baseType()->isValueType(), "");
+ Whiskers templ(R"({
+ let size := mul(length, <element_size>)
+ // cheap way of zero-initializing a memory range
+ codecopy(memptr, codesize(), size)
+ memptr := add(memptr, size)
+ })");
+ templ("element_size", to_string(_type.baseType()->memoryHeadSize()));
+ m_context.appendInlineAssembly(templ.render(), {"length", "memptr"});
+ }
+ else
+ {
+ // TODO: Potential optimization:
+ // When we create a new multi-dimensional dynamic array, each element
+ // is initialized to an empty array. It actually does not hurt
+ // to re-use exactly the same empty array for all elements. Currently,
+ // a new one is created each time.
+ auto repeat = m_context.newTag();
+ m_context << repeat;
+ pushZeroValue(*_type.baseType());
+ storeInMemoryDynamic(*_type.baseType());
+ m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1;
+ m_context << Instruction::SUB << Instruction::SWAP1;
+ m_context << Instruction::DUP2;
+ m_context.appendConditionalJumpTo(repeat);
+ }
m_context << Instruction::SWAP1 << Instruction::POP;
}
@@ -427,7 +625,7 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned)
leftShiftNumberOnStack(64);
}
-void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
+void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly)
{
m_context << m_context.functionEntryLabel(_function).pushTag();
// If there is a runtime context, we have to merge both labels into the same
@@ -435,9 +633,10 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
if (CompilerContext* rtc = m_context.runtimeContext())
{
leftShiftNumberOnStack(32);
- m_context <<
- rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
- Instruction::OR;
+ if (_runtimeOnly)
+ m_context <<
+ rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
+ Instruction::OR;
}
}
@@ -485,19 +684,17 @@ void CompilerUtils::convertType(
// clear for conversion to longer bytes
solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
FixedBytesType const& targetType = dynamic_cast<FixedBytesType const&>(_targetType);
- if (targetType.numBytes() > typeOnStack.numBytes() || _cleanupNeeded)
+ if (typeOnStack.numBytes() == 0 || targetType.numBytes() == 0)
+ m_context << Instruction::POP << u256(0);
+ else if (targetType.numBytes() > typeOnStack.numBytes() || _cleanupNeeded)
{
- if (typeOnStack.numBytes() == 0)
- m_context << Instruction::POP << u256(0);
- else
- {
- m_context << ((u256(1) << (256 - typeOnStack.numBytes() * 8)) - 1);
- m_context << Instruction::NOT << Instruction::AND;
- }
+ int bytes = min(typeOnStack.numBytes(), targetType.numBytes());
+ m_context << ((u256(1) << (256 - bytes * 8)) - 1);
+ m_context << Instruction::NOT << Instruction::AND;
}
}
- }
break;
+ }
case Type::Category::Enum:
solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, "");
if (enumOverflowCheckPending)
@@ -506,6 +703,7 @@ void CompilerUtils::convertType(
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
if (_asPartOfArgumentDecoding)
+ // TODO: error message?
m_context.appendConditionalRevert();
else
m_context.appendConditionalInvalid();
@@ -598,8 +796,9 @@ void CompilerUtils::convertType(
bytesConstRef data(value);
if (targetTypeCategory == Type::Category::FixedBytes)
{
+ int const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes();
solAssert(data.size() <= 32, "");
- m_context << h256::Arith(h256(data, h256::AlignLeft));
+ m_context << (h256::Arith(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes))));
}
else if (targetTypeCategory == Type::Category::Array)
{
@@ -873,6 +1072,13 @@ void CompilerUtils::pushZeroValue(Type const& _type)
return;
}
solAssert(referenceType->location() == DataLocation::Memory, "");
+ if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
+ if (arrayType->isDynamicallySized())
+ {
+ // Push a memory location that is (hopefully) always zero.
+ pushZeroPointer();
+ return;
+ }
TypePointer type = _type.shared_from_this();
m_context.callLowLevelFunction(
@@ -893,13 +1099,8 @@ void CompilerUtils::pushZeroValue(Type const& _type)
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(type.get()))
{
- if (arrayType->isDynamicallySized())
- {
- // zero length
- _context << u256(0);
- utils.storeInMemoryDynamic(IntegerType(256));
- }
- else if (arrayType->length() > 0)
+ solAssert(!arrayType->isDynamicallySized(), "");
+ if (arrayType->length() > 0)
{
_context << arrayType->length() << Instruction::SWAP1;
// stack: items_to_do memory_pos
@@ -916,6 +1117,11 @@ void CompilerUtils::pushZeroValue(Type const& _type)
);
}
+void CompilerUtils::pushZeroPointer()
+{
+ m_context << u256(zeroPointer);
+}
+
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 3cde281b..476a7559 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -54,6 +54,13 @@ public:
/// Stack post: <size> <mem_start>
void toSizeAfterFreeMemoryPointer();
+ /// Appends code that performs a revert, providing the given string data.
+ /// Will also append an error signature corresponding to Error(string).
+ /// @param _argumentType the type of the string argument, will be converted to memory string.
+ /// Stack pre: string data
+ /// Stack post:
+ void revertWithStringData(Type const& _argumentType);
+
/// Loads data from memory to the stack.
/// @param _offset offset in memory (or calldata)
/// @param _type data type to load
@@ -88,6 +95,15 @@ public:
/// Stack post: (memory_offset+length)
void storeInMemoryDynamic(Type const& _type, bool _padToWords = true);
+ /// Creates code that unpacks the arguments according to their types specified by a vector of TypePointers.
+ /// From memory if @a _fromMemory is true, otherwise from call data.
+ /// Calls revert if @a _revertOnOutOfBounds is true and the supplied size is shorter
+ /// than the static data requirements or if dynamic data pointers reach outside of the
+ /// area. Also has a hard cap of 0x100000000 for any given length/offset field.
+ /// Stack pre: <source_offset> <length>
+ /// Stack post: <value0> <value1> ... <valuen>
+ void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false, bool _revertOnOutOfBounds = false);
+
/// Copies values (of types @a _givenTypes) given on the stack to a location in memory given
/// at the stack top, encoding them according to the ABI as the given types @a _targetTypes.
/// Removes the values from the stack and leaves the updated memory pointer.
@@ -149,7 +165,7 @@ public:
/// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true,
/// the data is taken from memory instead of from calldata.
/// Can allocate memory.
- /// Stack pre: <source_offset>
+ /// Stack pre: <source_offset> <length>
/// Stack post: <value0> <value1> ... <valuen>
void abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory = false);
@@ -179,7 +195,8 @@ public:
/// Appends code that combines the construction-time (if available) and runtime function
/// entry label of the given function into a single stack slot.
/// Note: This might cause the compilation queue of the runtime context to be extended.
- void pushCombinedFunctionEntryLabel(Declaration const& _function);
+ /// If @a _runtimeOnly, the entry label will include the runtime assembly tag.
+ void pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly = true);
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
@@ -200,6 +217,9 @@ public:
/// Creates a zero-value for the given type and puts it onto the stack. This might allocate
/// memory for memory references.
void pushZeroValue(Type const& _type);
+ /// Pushes a pointer to the stack that points to a (potentially shared) location in memory
+ /// that always contains a zero. It is not allowed to write there.
+ void pushZeroPointer();
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
@@ -245,6 +265,10 @@ public:
/// Position of the free-memory-pointer in memory;
static const size_t freeMemoryPointer;
+ /// Position of the memory slot that is always zero.
+ static const size_t zeroPointer;
+ /// Starting offset for memory available to the user (aka the contract).
+ static const size_t generalPurposeMemoryStart;
private:
/// Address of the precompiled identity contract.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 5a9498f0..0889ac7c 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -128,6 +128,7 @@ void ContractCompiler::appendCallValueCheck()
{
// Throw if function is not payable but call contained ether.
m_context << Instruction::CALLVALUE;
+ // TODO: error message?
m_context.appendConditionalRevert();
}
@@ -135,33 +136,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
{
solAssert(!_contract.isLibrary(), "Tried to initialize library.");
CompilerContext::LocationSetter locationSetter(m_context, _contract);
- // Determine the arguments that are used for the base constructors.
- std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
- for (ContractDefinition const* contract: bases)
- {
- if (FunctionDefinition const* constructor = contract->constructor())
- for (auto const& modifier: constructor->modifiers())
- {
- auto baseContract = dynamic_cast<ContractDefinition const*>(
- modifier->name()->annotation().referencedDeclaration);
- if (baseContract)
- if (m_baseArguments.count(baseContract->constructor()) == 0)
- m_baseArguments[baseContract->constructor()] = &modifier->arguments();
- }
- for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
- {
- ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
- base->name().annotation().referencedDeclaration
- );
- solAssert(baseContract, "");
+ m_baseArguments = &_contract.annotation().baseConstructorArguments;
- if (m_baseArguments.count(baseContract->constructor()) == 0)
- m_baseArguments[baseContract->constructor()] = &base->arguments();
- }
- }
// Initialization of state variables in base-to-derived order.
- for (ContractDefinition const* contract: boost::adaptors::reverse(bases))
+ for (ContractDefinition const* contract: boost::adaptors::reverse(
+ _contract.annotation().linearizedBaseContracts
+ ))
initializeStateVariables(*contract);
if (FunctionDefinition const* constructor = _contract.constructor())
@@ -235,9 +216,16 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc
FunctionType constructorType(_constructor);
if (!constructorType.parameterTypes().empty())
{
- solAssert(m_baseArguments.count(&_constructor), "");
- std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
+ solAssert(m_baseArguments, "");
+ solAssert(m_baseArguments->count(&_constructor), "");
+ std::vector<ASTPointer<Expression>> const* arguments = nullptr;
+ ASTNode const* baseArgumentNode = m_baseArguments->at(&_constructor);
+ if (auto inheritanceSpecifier = dynamic_cast<InheritanceSpecifier const*>(baseArgumentNode))
+ arguments = inheritanceSpecifier->arguments();
+ else if (auto modifierInvocation = dynamic_cast<ModifierInvocation const*>(baseArgumentNode))
+ arguments = modifierInvocation->arguments();
solAssert(arguments, "");
+ solAssert(arguments->size() == constructorType.parameterTypes().size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]);
}
@@ -278,9 +266,10 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor)
m_context.appendProgramSize();
m_context << Instruction::DUP4 << Instruction::CODECOPY;
m_context << Instruction::DUP2 << Instruction::ADD;
+ m_context << Instruction::DUP1;
CompilerUtils(m_context).storeFreeMemoryPointer();
// stack: <memptr>
- appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true);
+ CompilerUtils(m_context).abiDecode(FunctionType(_constructor).parameterTypes(), true);
}
_constructor.accept(*this);
}
@@ -339,6 +328,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << Instruction::STOP;
}
else
+ // TODO: error message here?
m_context.appendRevert();
for (auto const& it: interfaceFunctions)
@@ -367,7 +357,8 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
{
// Parameter for calldataUnpacker
m_context << CompilerUtils::dataStartOffset;
- appendCalldataUnpacker(functionType->parameterTypes());
+ m_context << Instruction::DUP1 << Instruction::CALLDATASIZE << Instruction::SUB;
+ CompilerUtils(m_context).abiDecode(functionType->parameterTypes());
}
m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
m_context << returnTag;
@@ -382,105 +373,6 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
}
}
-void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory)
-{
- // We do not check the calldata size, everything is zero-padded
-
- if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
- {
- // Use the new JULIA-based decoding function
- auto stackHeightBefore = m_context.stackHeight();
- CompilerUtils(m_context).abiDecodeV2(_typeParameters, _fromMemory);
- solAssert(m_context.stackHeight() - stackHeightBefore == CompilerUtils(m_context).sizeOnStack(_typeParameters) - 1, "");
- return;
- }
-
- //@todo this does not yet support nested dynamic arrays
-
- // Retain the offset pointer as base_offset, the point from which the data offsets are computed.
- m_context << Instruction::DUP1;
- for (TypePointer const& parameterType: _typeParameters)
- {
- // stack: v1 v2 ... v(k-1) base_offset current_offset
- TypePointer type = parameterType->decodingType();
- solUnimplementedAssert(type, "No decoding type found.");
- if (type->category() == Type::Category::Array)
- {
- auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
- solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
- if (_fromMemory)
- {
- solUnimplementedAssert(
- arrayType.baseType()->isValueType(),
- "Nested memory arrays not yet implemented here."
- );
- // @todo If base type is an array or struct, it is still calldata-style encoded, so
- // we would have to convert it like below.
- solAssert(arrayType.location() == DataLocation::Memory, "");
- if (arrayType.isDynamicallySized())
- {
- // compute data pointer
- m_context << Instruction::DUP1 << Instruction::MLOAD;
- m_context << Instruction::DUP3 << Instruction::ADD;
- m_context << Instruction::SWAP2 << Instruction::SWAP1;
- m_context << u256(0x20) << Instruction::ADD;
- }
- else
- {
- m_context << Instruction::SWAP1 << Instruction::DUP2;
- m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD;
- }
- }
- else
- {
- // first load from calldata and potentially convert to memory if arrayType is memory
- TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false);
- if (calldataType->isDynamicallySized())
- {
- // put on stack: data_pointer length
- CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory);
- // stack: base_offset data_offset next_pointer
- m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD;
- // stack: base_offset next_pointer data_pointer
- // retrieve length
- CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true);
- // stack: base_offset next_pointer length data_pointer
- m_context << Instruction::SWAP2;
- // stack: base_offset data_pointer length next_pointer
- }
- else
- {
- // leave the pointer on the stack
- m_context << Instruction::DUP1;
- m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD;
- }
- if (arrayType.location() == DataLocation::Memory)
- {
- // stack: base_offset calldata_ref [length] next_calldata
- // copy to memory
- // move calldata type up again
- CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack());
- CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true);
- // fetch next pointer again
- CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack());
- }
- // move base_offset up
- CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
- m_context << Instruction::SWAP1;
- }
- }
- else
- {
- solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
- CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
- CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack());
- m_context << Instruction::SWAP1;
- }
- // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset
- }
- m_context << Instruction::POP << Instruction::POP;
-}
-
void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary)
{
CompilerUtils utils(m_context);
@@ -1002,15 +894,21 @@ void ContractCompiler::appendModifierOrFunctionCode()
appendModifierOrFunctionCode();
else
{
- ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name());
+ ModifierDefinition const& nonVirtualModifier = dynamic_cast<ModifierDefinition const&>(
+ *modifierInvocation->name()->annotation().referencedDeclaration
+ );
+ ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier);
CompilerContext::LocationSetter locationSetter(m_context, modifier);
- solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), "");
+ std::vector<ASTPointer<Expression>> const& modifierArguments =
+ modifierInvocation->arguments() ? *modifierInvocation->arguments() : std::vector<ASTPointer<Expression>>();
+
+ solAssert(modifier.parameters().size() == modifierArguments.size(), "");
for (unsigned i = 0; i < modifier.parameters().size(); ++i)
{
m_context.addVariable(*modifier.parameters()[i]);
addedVariables.push_back(modifier.parameters()[i].get());
compileExpression(
- *modifierInvocation->arguments()[i],
+ *modifierArguments[i],
modifier.parameters()[i]->annotation().type
);
}
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 8559ea58..02a3452f 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -90,10 +90,6 @@ private:
void appendDelegatecallCheck();
void appendFunctionSelector(ContractDefinition const& _contract);
void appendCallValueCheck();
- /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
- /// From memory if @a _fromMemory is true, otherwise from call data.
- /// Expects source offset on the stack, which is removed.
- void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary);
void registerStateVariables(ContractDefinition const& _contract);
@@ -139,7 +135,7 @@ private:
FunctionDefinition const* m_currentFunction = nullptr;
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
// arguments for base constructors, filled in derived-to-base order
- std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
+ std::map<FunctionDefinition const*, ASTNode const*> const* m_baseArguments;
};
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 7162cb0d..3cf46a9d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -33,6 +33,8 @@
#include <libsolidity/codegen/LValue.h>
#include <libevmasm/GasMeter.h>
+#include <libdevcore/Whiskers.h>
+
using namespace std;
namespace dev
@@ -139,8 +141,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
utils().popStackSlots(paramTypes.size() - 1);
}
unsigned retSizeOnStack = 0;
- solAssert(accessorType.returnParameterTypes().size() >= 1, "");
- auto const& returnTypes = accessorType.returnParameterTypes();
+ auto returnTypes = accessorType.returnParameterTypes();
+ solAssert(returnTypes.size() >= 1, "");
if (StructType const* structType = dynamic_cast<StructType const*>(returnType.get()))
{
// remove offset
@@ -518,7 +520,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments[i]->accept(*this);
utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]);
}
- _functionCall.expression().accept(*this);
+
+ {
+ bool shortcutTaken = false;
+ if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
+ if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
+ {
+ // Do not directly visit the identifier, because this way, we can avoid
+ // the runtime entry label to be created at the creation time context.
+ CompilerContext::LocationSetter locationSetter2(m_context, *identifier);
+ utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false);
+ shortcutTaken = true;
+ }
+
+ if (!shortcutTaken)
+ _functionCall.expression().accept(*this);
+ }
+
unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes());
if (function.bound())
{
@@ -592,7 +610,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::CREATE;
// Check if zero (out of stack or not enough balance).
m_context << Instruction::DUP1 << Instruction::ISZERO;
- m_context.appendConditionalRevert();
+ // TODO: Can we bubble up here? There might be different reasons for failure, I think.
+ m_context.appendConditionalRevert(true);
if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP;
break;
@@ -654,8 +673,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (function.kind() == FunctionType::Kind::Transfer)
{
// Check if zero (out of stack or not enough balance).
+ // TODO: bubble up here, but might also be different error.
m_context << Instruction::ISZERO;
- m_context.appendConditionalRevert();
+ m_context.appendConditionalRevert(true);
}
break;
case FunctionType::Kind::Selfdestruct:
@@ -664,8 +684,19 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << Instruction::SELFDESTRUCT;
break;
case FunctionType::Kind::Revert:
- m_context.appendRevert();
+ {
+ if (!arguments.empty())
+ {
+ // function-sel(Error(string)) + encoding
+ solAssert(arguments.size() == 1, "");
+ solAssert(function.parameterTypes().size() == 1, "");
+ arguments.front()->accept(*this);
+ utils().revertWithStringData(*arguments.front()->annotation().type);
+ }
+ else
+ m_context.appendRevert();
break;
+ }
case FunctionType::Kind::SHA3:
{
TypePointers argumentTypes;
@@ -805,24 +836,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
function.kind() == FunctionType::Kind::ArrayPush ?
make_shared<ArrayType>(DataLocation::Storage, paramType) :
make_shared<ArrayType>(DataLocation::Storage);
- // get the current length
- ArrayUtils(m_context).retrieveLength(*arrayType);
- m_context << Instruction::DUP1;
- // stack: ArrayReference currentLength currentLength
- m_context << u256(1) << Instruction::ADD;
- // stack: ArrayReference currentLength newLength
- m_context << Instruction::DUP3 << Instruction::DUP2;
- ArrayUtils(m_context).resizeDynamicArray(*arrayType);
- m_context << Instruction::SWAP2 << Instruction::SWAP1;
- // stack: newLength ArrayReference oldLength
- ArrayUtils(m_context).accessIndex(*arrayType, false);
- // stack: newLength storageSlot slotOffset
+ // stack: ArrayReference
arguments[0]->accept(*this);
+ TypePointer const& argType = arguments[0]->annotation().type;
+ // stack: ArrayReference argValue
+ utils().moveToStackTop(argType->sizeOnStack(), 1);
+ // stack: argValue ArrayReference
+ m_context << Instruction::DUP1;
+ ArrayUtils(m_context).incrementDynamicArraySize(*arrayType);
+ // stack: argValue ArrayReference newLength
+ m_context << Instruction::SWAP1;
+ // stack: argValue newLength ArrayReference
+ m_context << u256(1) << Instruction::DUP3 << Instruction::SUB;
+ // stack: argValue newLength ArrayReference (newLength-1)
+ ArrayUtils(m_context).accessIndex(*arrayType, false);
+ // stack: argValue newLength storageSlot slotOffset
+ utils().moveToStackTop(3, argType->sizeOnStack());
// stack: newLength storageSlot slotOffset argValue
TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
solAssert(type, "");
- utils().convertType(*arguments[0]->annotation().type, *type);
+ utils().convertType(*argType, *type);
utils().moveToStackTop(1 + type->sizeOnStack());
utils().moveToStackTop(1 + type->sizeOnStack());
// stack: newLength argValue storageSlot slotOffset
@@ -834,8 +868,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case FunctionType::Kind::ObjectCreation:
{
- // Will allocate at the end of memory (MSIZE) and not write at all unless the base
- // type is dynamically sized.
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
_functionCall.expression().accept(*this);
solAssert(arguments.size() == 1, "");
@@ -845,15 +877,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().convertType(*arguments[0]->annotation().type, IntegerType(256));
// Stack: requested_length
- // Allocate at max(MSIZE, freeMemoryPointer)
utils().fetchFreeMemoryPointer();
- m_context << Instruction::DUP1 << Instruction::MSIZE;
- m_context << Instruction::LT;
- auto initialise = m_context.appendConditionalJump();
- // Free memory pointer does not point to empty memory, use MSIZE.
- m_context << Instruction::POP;
- m_context << Instruction::MSIZE;
- m_context << initialise;
// Stack: requested_length memptr
m_context << Instruction::SWAP1;
@@ -878,13 +902,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// Check if length is zero
m_context << Instruction::DUP1 << Instruction::ISZERO;
auto skipInit = m_context.appendConditionalJump();
-
- // We only have to initialise if the base type is a not a value type.
- if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get()))
- {
- m_context << Instruction::DUP2 << u256(32) << Instruction::ADD;
- utils().zeroInitialiseMemoryArray(arrayType);
- }
+ // Always initialize because the free memory pointer might point at
+ // a dirty memory area.
+ m_context << Instruction::DUP2 << u256(32) << Instruction::ADD;
+ utils().zeroInitialiseMemoryArray(arrayType);
m_context << skipInit;
m_context << Instruction::POP;
break;
@@ -894,16 +915,130 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
arguments.front()->accept(*this);
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false);
+ if (arguments.size() > 1)
+ {
+ // Users probably expect the second argument to be evaluated
+ // even if the condition is false, as would be the case for an actual
+ // function call.
+ solAssert(arguments.size() == 2, "");
+ solAssert(function.kind() == FunctionType::Kind::Require, "");
+ arguments.at(1)->accept(*this);
+ utils().moveIntoStack(1, arguments.at(1)->annotation().type->sizeOnStack());
+ }
+ // Stack: <error string (unconverted)> <condition>
// jump if condition was met
m_context << Instruction::ISZERO << Instruction::ISZERO;
auto success = m_context.appendConditionalJump();
if (function.kind() == FunctionType::Kind::Assert)
// condition was not met, flag an error
m_context.appendInvalid();
+ else if (arguments.size() > 1)
+ utils().revertWithStringData(*arguments.at(1)->annotation().type);
else
m_context.appendRevert();
// the success branch
m_context << success;
+ if (arguments.size() > 1)
+ utils().popStackElement(*arguments.at(1)->annotation().type);
+ break;
+ }
+ case FunctionType::Kind::ABIEncode:
+ case FunctionType::Kind::ABIEncodePacked:
+ case FunctionType::Kind::ABIEncodeWithSelector:
+ case FunctionType::Kind::ABIEncodeWithSignature:
+ {
+ bool const isPacked = function.kind() == FunctionType::Kind::ABIEncodePacked;
+ bool const hasSelectorOrSignature =
+ function.kind() == FunctionType::Kind::ABIEncodeWithSelector ||
+ function.kind() == FunctionType::Kind::ABIEncodeWithSignature;
+
+ TypePointers argumentTypes;
+ TypePointers targetTypes;
+ for (unsigned i = 0; i < arguments.size(); ++i)
+ {
+ arguments[i]->accept(*this);
+ // Do not keep the selector as part of the ABI encoded args
+ if (!hasSelectorOrSignature || i > 0)
+ argumentTypes.push_back(arguments[i]->annotation().type);
+ }
+ utils().fetchFreeMemoryPointer();
+ // stack now: [<selector>] <arg1> .. <argN> <free_mem>
+
+ // adjust by 32(+4) bytes to accommodate the length(+selector)
+ m_context << u256(32 + (hasSelectorOrSignature ? 4 : 0)) << Instruction::ADD;
+ // stack now: [<selector>] <arg1> .. <argN> <data_encoding_area_start>
+
+ if (isPacked)
+ {
+ solAssert(!function.padArguments(), "");
+ utils().packedEncode(argumentTypes, TypePointers());
+ }
+ else
+ {
+ solAssert(function.padArguments(), "");
+ utils().abiEncode(argumentTypes, TypePointers());
+ }
+ utils().fetchFreeMemoryPointer();
+ // stack: [<selector>] <data_encoding_area_end> <bytes_memory_ptr>
+
+ // size is end minus start minus length slot
+ m_context.appendInlineAssembly(R"({
+ mstore(mem_ptr, sub(sub(mem_end, mem_ptr), 0x20))
+ })", {"mem_end", "mem_ptr"});
+ m_context << Instruction::SWAP1;
+ utils().storeFreeMemoryPointer();
+ // stack: [<selector>] <memory ptr>
+
+ if (hasSelectorOrSignature)
+ {
+ // stack: <selector> <memory pointer>
+ solAssert(arguments.size() >= 1, "");
+ TypePointer const& selectorType = arguments[0]->annotation().type;
+ utils().moveIntoStack(selectorType->sizeOnStack());
+ TypePointer dataOnStack = selectorType;
+ // stack: <memory pointer> <selector>
+ if (function.kind() == FunctionType::Kind::ABIEncodeWithSignature)
+ {
+ // hash the signature
+ if (auto const* stringType = dynamic_cast<StringLiteralType const*>(selectorType.get()))
+ {
+ FixedHash<4> hash(dev::keccak256(stringType->value()));
+ m_context << (u256(FixedHash<4>::Arith(hash)) << (256 - 32));
+ dataOnStack = make_shared<FixedBytesType>(4);
+ }
+ else
+ {
+ utils().fetchFreeMemoryPointer();
+ // stack: <memory pointer> <selector> <free mem ptr>
+ utils().packedEncode(TypePointers{selectorType}, TypePointers());
+ utils().toSizeAfterFreeMemoryPointer();
+ m_context << Instruction::KECCAK256;
+ // stack: <memory pointer> <hash>
+
+ dataOnStack = make_shared<FixedBytesType>(32);
+ }
+ }
+ else
+ {
+ solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, "");
+ }
+
+ utils().convertType(*dataOnStack, FixedBytesType(4), true);
+
+ // stack: <memory pointer> <selector>
+
+ // load current memory, mask and combine the selector
+ string mask = formatNumber((u256(-1) >> 32));
+ m_context.appendInlineAssembly(R"({
+ let data_start := add(mem_ptr, 0x20)
+ let data := mload(data_start)
+ let mask := )" + mask + R"(
+ mstore(data_start, or(and(data, mask), selector))
+ })", {"mem_ptr", "selector"});
+ m_context << Instruction::POP;
+ }
+
+ // stack now: <memory pointer>
break;
}
case FunctionType::Kind::GasLeft:
@@ -1147,6 +1282,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
else if (member == "sig")
m_context << u256(0) << Instruction::CALLDATALOAD
<< (u256(0xffffffff) << (256 - 32)) << Instruction::AND;
+ else if (member == "blockhash")
+ {
+ }
else
solAssert(false, "Unknown magic member.");
break;
@@ -1356,6 +1494,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
+ // If the identifier is called right away, this code is executed in visit(FunctionCall...), because
+ // we want to avoid having a reference to the runtime function entry point in the
+ // constructor context, since this would force the compiler to include unreferenced
+ // internal functions in the runtime contex.
utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef));
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
appendVariable(*variable, static_cast<Expression const&>(_identifier));
@@ -1615,15 +1757,27 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context.experimentalFeatureActive(ExperimentalFeature::V050) &&
m_context.evmVersion().hasStaticCall();
+ bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
unsigned retSize = 0;
+ TypePointers returnTypes;
if (returnSuccessCondition)
retSize = 0; // return value actually is success condition
+ else if (haveReturndatacopy)
+ returnTypes = _functionType.returnParameterTypes();
else
- for (auto const& retType: _functionType.returnParameterTypes())
+ returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes();
+
+ bool dynamicReturnSize = false;
+ for (auto const& retType: returnTypes)
+ if (retType->isDynamicallyEncoded())
{
- solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call.");
- retSize += retType->calldataEncodedSize();
+ solAssert(haveReturndatacopy, "");
+ dynamicReturnSize = true;
+ retSize = 0;
+ break;
}
+ else
+ retSize += retType->calldataEncodedSize();
// Evaluate arguments.
TypePointers argumentTypes;
@@ -1755,6 +1909,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
+ // TODO: error message?
m_context.appendConditionalRevert();
existenceChecked = true;
}
@@ -1797,7 +1952,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
{
//Propagate error condition (if CALL pushes 0 on stack).
m_context << Instruction::ISZERO;
- m_context.appendConditionalRevert();
+ m_context.appendConditionalRevert(true);
}
utils().popStackSlots(remainsSize);
@@ -1821,20 +1976,42 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().fetchFreeMemoryPointer();
m_context << Instruction::SUB << Instruction::MLOAD;
}
- else if (!_functionType.returnParameterTypes().empty())
+ else if (!returnTypes.empty())
{
utils().fetchFreeMemoryPointer();
- bool memoryNeeded = false;
- for (auto const& retType: _functionType.returnParameterTypes())
+ // Stack: return_data_start
+
+ // The old decoder did not allocate any memory (i.e. did not touch the free
+ // memory pointer), but kept references to the return data for
+ // (statically-sized) arrays
+ bool needToUpdateFreeMemoryPtr = false;
+ if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
+ needToUpdateFreeMemoryPtr = true;
+ else
+ for (auto const& retType: returnTypes)
+ if (dynamic_cast<ReferenceType const*>(retType.get()))
+ needToUpdateFreeMemoryPtr = true;
+
+ // Stack: return_data_start
+ if (dynamicReturnSize)
{
- utils().loadFromMemoryDynamic(*retType, false, true, true);
- if (dynamic_cast<ReferenceType const*>(retType.get()))
- memoryNeeded = true;
+ solAssert(haveReturndatacopy, "");
+ m_context.appendInlineAssembly("{ returndatacopy(return_data_start, 0, returndatasize()) }", {"return_data_start"});
}
- if (memoryNeeded)
- utils().storeFreeMemoryPointer();
else
- m_context << Instruction::POP;
+ solAssert(retSize > 0, "");
+ // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
+ // This ensures it can catch badly formatted input from external calls.
+ m_context << (haveReturndatacopy ? eth::AssemblyItem(Instruction::RETURNDATASIZE) : u256(retSize));
+ // Stack: return_data_start return_data_size
+ if (needToUpdateFreeMemoryPtr)
+ m_context.appendInlineAssembly(R"({
+ // round size to the next multiple of 32
+ let newMem := add(start, and(add(size, 0x1f), not(0x1f)))
+ mstore(0x40, newMem)
+ })", {"start", "size"});
+
+ utils().abiDecode(returnTypes, true, true);
}
}
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 1da5b291..8f4abdc2 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -205,7 +205,7 @@ void SMTChecker::endVisit(Assignment const& _assignment)
_assignment.location(),
"Assertion checker does not yet implement compound assignment."
);
- else if (_assignment.annotation().type->category() != Type::Category::Integer)
+ else if (!SSAVariable::isSupportedType(_assignment.annotation().type->category()))
m_errorReporter.warning(
_assignment.location(),
"Assertion checker does not yet implement type " + _assignment.annotation().type->toString()
@@ -266,14 +266,15 @@ void SMTChecker::endVisit(UnaryOperation const& _op)
{
case Token::Not: // !
{
- solAssert(_op.annotation().type->category() == Type::Category::Bool, "");
+ solAssert(SSAVariable::isBool(_op.annotation().type->category()), "");
defineExpr(_op, !expr(_op.subExpression()));
break;
}
case Token::Inc: // ++ (pre- or postfix)
case Token::Dec: // -- (pre- or postfix)
{
- solAssert(_op.annotation().type->category() == Type::Category::Integer, "");
+
+ solAssert(SSAVariable::isInteger(_op.annotation().type->category()), "");
solAssert(_op.subExpression().annotation().lValueRequested, "");
if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_op.subExpression()))
{
@@ -370,7 +371,7 @@ void SMTChecker::endVisit(Identifier const& _identifier)
{
// Will be translated as part of the node that requested the lvalue.
}
- else if (SSAVariable::supportedType(_identifier.annotation().type.get()))
+ else if (SSAVariable::isSupportedType(_identifier.annotation().type->category()))
defineExpr(_identifier, currentValue(*decl));
else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
{
@@ -444,21 +445,37 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
void SMTChecker::compareOperation(BinaryOperation const& _op)
{
solAssert(_op.annotation().commonType, "");
- if (_op.annotation().commonType->category() == Type::Category::Integer)
+ if (SSAVariable::isSupportedType(_op.annotation().commonType->category()))
{
smt::Expression left(expr(_op.leftExpression()));
smt::Expression right(expr(_op.rightExpression()));
Token::Value op = _op.getOperator();
- smt::Expression value = (
- op == Token::Equal ? (left == right) :
- op == Token::NotEqual ? (left != right) :
- op == Token::LessThan ? (left < right) :
- op == Token::LessThanOrEqual ? (left <= right) :
- op == Token::GreaterThan ? (left > right) :
- /*op == Token::GreaterThanOrEqual*/ (left >= right)
- );
+ shared_ptr<smt::Expression> value;
+ if (SSAVariable::isInteger(_op.annotation().commonType->category()))
+ {
+ value = make_shared<smt::Expression>(
+ op == Token::Equal ? (left == right) :
+ op == Token::NotEqual ? (left != right) :
+ op == Token::LessThan ? (left < right) :
+ op == Token::LessThanOrEqual ? (left <= right) :
+ op == Token::GreaterThan ? (left > right) :
+ /*op == Token::GreaterThanOrEqual*/ (left >= right)
+ );
+ }
+ else // Bool
+ {
+ solAssert(SSAVariable::isBool(_op.annotation().commonType->category()), "");
+ value = make_shared<smt::Expression>(
+ op == Token::Equal ? (left == right) :
+ op == Token::NotEqual ? (left != right) :
+ op == Token::LessThan ? (!left && right) :
+ op == Token::LessThanOrEqual ? (!left || right) :
+ op == Token::GreaterThan ? (left && !right) :
+ /*op == Token::GreaterThanOrEqual*/ (left || !right)
+ );
+ }
// TODO: check that other values for op are not possible.
- defineExpr(_op, value);
+ defineExpr(_op, *value);
}
else
m_errorReporter.warning(
@@ -728,10 +745,10 @@ void SMTChecker::mergeVariables(vector<Declaration const*> const& _variables, sm
bool SMTChecker::createVariable(VariableDeclaration const& _varDecl)
{
- if (SSAVariable::supportedType(_varDecl.type().get()))
+ if (SSAVariable::isSupportedType(_varDecl.type()->category()))
{
solAssert(m_variables.count(&_varDecl) == 0, "");
- m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface));
+ m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface));
return true;
}
else
diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp
index 4e6bcbcb..f3213e03 100644
--- a/libsolidity/formal/SSAVariable.cpp
+++ b/libsolidity/formal/SSAVariable.cpp
@@ -17,6 +17,7 @@
#include <libsolidity/formal/SSAVariable.h>
+#include <libsolidity/formal/SymbolicBoolVariable.h>
#include <libsolidity/formal/SymbolicIntVariable.h>
#include <libsolidity/ast/AST.h>
@@ -26,23 +27,35 @@ using namespace dev;
using namespace dev::solidity;
SSAVariable::SSAVariable(
- Declaration const* _decl,
+ Declaration const& _decl,
smt::SolverInterface& _interface
)
{
resetIndex();
- if (dynamic_cast<IntegerType const*>(_decl->type().get()))
+ if (isInteger(_decl.type()->category()))
m_symbolicVar = make_shared<SymbolicIntVariable>(_decl, _interface);
+ else if (isBool(_decl.type()->category()))
+ m_symbolicVar = make_shared<SymbolicBoolVariable>(_decl, _interface);
else
{
solAssert(false, "");
}
}
-bool SSAVariable::supportedType(Type const* _decl)
+bool SSAVariable::isSupportedType(Type::Category _category)
{
- return dynamic_cast<IntegerType const*>(_decl);
+ return isInteger(_category) || isBool(_category);
+}
+
+bool SSAVariable::isInteger(Type::Category _category)
+{
+ return _category == Type::Category::Integer;
+}
+
+bool SSAVariable::isBool(Type::Category _category)
+{
+ return _category == Type::Category::Bool;
}
void SSAVariable::resetIndex()
diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h
index 275e8590..bf5dae3b 100644
--- a/libsolidity/formal/SSAVariable.h
+++ b/libsolidity/formal/SSAVariable.h
@@ -37,7 +37,7 @@ public:
/// @param _decl Used to determine the type and forwarded to the symbolic var.
/// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver.
SSAVariable(
- Declaration const* _decl,
+ Declaration const& _decl,
smt::SolverInterface& _interface
);
@@ -68,8 +68,10 @@ public:
void setZeroValue();
void setUnknownValue();
- /// So far Int is supported.
- static bool supportedType(Type const* _decl);
+ /// So far Int and Bool are supported.
+ static bool isSupportedType(Type::Category _category);
+ static bool isInteger(Type::Category _category);
+ static bool isBool(Type::Category _category);
private:
smt::Expression valueAtSequence(int _seq) const
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index 88487310..0bdebb6c 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -46,7 +46,8 @@ enum class Sort
{
Int,
Bool,
- IntIntFun // Function of one Int returning a single Int
+ IntIntFun, // Function of one Int returning a single Int
+ IntBoolFun // Function of one Int returning a single Bool
};
/// C++ representation of an SMTLIB2 expression.
@@ -132,10 +133,22 @@ public:
Expression operator()(Expression _a) const
{
solAssert(
- sort == Sort::IntIntFun && arguments.empty(),
+ arguments.empty(),
"Attempted function application to non-function."
);
- return Expression(name, _a, Sort::Int);
+ switch (sort)
+ {
+ case Sort::IntIntFun:
+ return Expression(name, _a, Sort::Int);
+ case Sort::IntBoolFun:
+ return Expression(name, _a, Sort::Bool);
+ default:
+ solAssert(
+ false,
+ "Attempted function application to invalid type."
+ );
+ break;
+ }
}
std::string const name;
@@ -167,9 +180,18 @@ public:
virtual Expression newFunction(std::string _name, Sort _domain, Sort _codomain)
{
- solAssert(_domain == Sort::Int && _codomain == Sort::Int, "Function sort not supported.");
+ solAssert(_domain == Sort::Int, "Function sort not supported.");
// Subclasses should do something here
- return Expression(std::move(_name), {}, Sort::IntIntFun);
+ switch (_codomain)
+ {
+ case Sort::Int:
+ return Expression(std::move(_name), {}, Sort::IntIntFun);
+ case Sort::Bool:
+ return Expression(std::move(_name), {}, Sort::IntBoolFun);
+ default:
+ solAssert(false, "Function sort not supported.");
+ break;
+ }
}
virtual Expression newInteger(std::string _name)
{
diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp
new file mode 100644
index 00000000..5cf22d7d
--- /dev/null
+++ b/libsolidity/formal/SymbolicBoolVariable.cpp
@@ -0,0 +1,47 @@
+/*
+ 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/formal/SymbolicBoolVariable.h>
+
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+SymbolicBoolVariable::SymbolicBoolVariable(
+ Declaration const& _decl,
+ smt::SolverInterface&_interface
+):
+ SymbolicVariable(_decl, _interface)
+{
+ solAssert(m_declaration.type()->category() == Type::Category::Bool, "");
+}
+
+smt::Expression SymbolicBoolVariable::valueAtSequence(int _seq) const
+{
+ return m_interface.newBool(uniqueSymbol(_seq));
+}
+
+void SymbolicBoolVariable::setZeroValue(int _seq)
+{
+ m_interface.addAssertion(valueAtSequence(_seq) == smt::Expression(false));
+}
+
+void SymbolicBoolVariable::setUnknownValue(int)
+{
+}
diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h
new file mode 100644
index 00000000..678f97d9
--- /dev/null
+++ b/libsolidity/formal/SymbolicBoolVariable.h
@@ -0,0 +1,50 @@
+/*
+ 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/formal/SymbolicVariable.h>
+
+#include <libsolidity/ast/Types.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Specialization of SymbolicVariable for Bool
+ */
+class SymbolicBoolVariable: public SymbolicVariable
+{
+public:
+ SymbolicBoolVariable(
+ Declaration const& _decl,
+ smt::SolverInterface& _interface
+ );
+
+ /// Sets the var to false.
+ void setZeroValue(int _seq);
+ /// Does nothing since the SMT solver already knows the valid values.
+ void setUnknownValue(int _seq);
+
+protected:
+ smt::Expression valueAtSequence(int _seq) const;
+};
+
+}
+}
diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp
index d08dc155..5e71fdcc 100644
--- a/libsolidity/formal/SymbolicIntVariable.cpp
+++ b/libsolidity/formal/SymbolicIntVariable.cpp
@@ -24,13 +24,17 @@ using namespace dev;
using namespace dev::solidity;
SymbolicIntVariable::SymbolicIntVariable(
- Declaration const* _decl,
+ Declaration const& _decl,
smt::SolverInterface& _interface
):
SymbolicVariable(_decl, _interface)
{
- solAssert(m_declaration->type()->category() == Type::Category::Integer, "");
- m_expression = make_shared<smt::Expression>(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int));
+ solAssert(m_declaration.type()->category() == Type::Category::Integer, "");
+}
+
+smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const
+{
+ return m_interface.newInteger(uniqueSymbol(_seq));
}
void SymbolicIntVariable::setZeroValue(int _seq)
@@ -40,7 +44,7 @@ void SymbolicIntVariable::setZeroValue(int _seq)
void SymbolicIntVariable::setUnknownValue(int _seq)
{
- auto const& intType = dynamic_cast<IntegerType const&>(*m_declaration->type());
+ auto const& intType = dynamic_cast<IntegerType const&>(*m_declaration.type());
m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType));
m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType));
}
diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h
index afa25f1b..d591e8db 100644
--- a/libsolidity/formal/SymbolicIntVariable.h
+++ b/libsolidity/formal/SymbolicIntVariable.h
@@ -33,7 +33,7 @@ class SymbolicIntVariable: public SymbolicVariable
{
public:
SymbolicIntVariable(
- Declaration const* _decl,
+ Declaration const& _decl,
smt::SolverInterface& _interface
);
@@ -44,6 +44,9 @@ public:
static smt::Expression minValue(IntegerType const& _t);
static smt::Expression maxValue(IntegerType const& _t);
+
+protected:
+ smt::Expression valueAtSequence(int _seq) const;
};
}
diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp
index 629049ea..caefa3a3 100644
--- a/libsolidity/formal/SymbolicVariable.cpp
+++ b/libsolidity/formal/SymbolicVariable.cpp
@@ -24,7 +24,7 @@ using namespace dev;
using namespace dev::solidity;
SymbolicVariable::SymbolicVariable(
- Declaration const* _decl,
+ Declaration const& _decl,
smt::SolverInterface& _interface
):
m_declaration(_decl),
@@ -32,9 +32,9 @@ SymbolicVariable::SymbolicVariable(
{
}
-string SymbolicVariable::uniqueSymbol() const
+string SymbolicVariable::uniqueSymbol(int _seq) const
{
- return m_declaration->name() + "_" + to_string(m_declaration->id());
+ return m_declaration.name() + "_" + to_string(m_declaration.id()) + "_" + to_string(_seq);
}
diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h
index 93258250..e4e4ea8d 100644
--- a/libsolidity/formal/SymbolicVariable.h
+++ b/libsolidity/formal/SymbolicVariable.h
@@ -37,7 +37,7 @@ class SymbolicVariable
{
public:
SymbolicVariable(
- Declaration const* _decl,
+ Declaration const& _decl,
smt::SolverInterface& _interface
);
@@ -46,7 +46,7 @@ public:
return valueAtSequence(_seq);
}
- std::string uniqueSymbol() const;
+ std::string uniqueSymbol(int _seq) const;
/// Sets the var to the default value of its type.
virtual void setZeroValue(int _seq) = 0;
@@ -55,13 +55,9 @@ public:
virtual void setUnknownValue(int _seq) = 0;
protected:
- smt::Expression valueAtSequence(int _seq) const
- {
- return (*m_expression)(_seq);
- }
+ virtual smt::Expression valueAtSequence(int _seq) const = 0;
- Declaration const* m_declaration;
- std::shared_ptr<smt::Expression> m_expression = nullptr;
+ Declaration const& m_declaration;
smt::SolverInterface& m_interface;
};
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index eacfca9c..4ff14aa2 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -164,85 +164,94 @@ bool CompilerStack::analyze()
resolveImports();
bool noErrors = true;
- SyntaxChecker syntaxChecker(m_errorReporter);
- for (Source const* source: m_sourceOrder)
- if (!syntaxChecker.checkSyntax(*source->ast))
- noErrors = false;
-
- DocStringAnalyser docStringAnalyser(m_errorReporter);
- for (Source const* source: m_sourceOrder)
- if (!docStringAnalyser.analyseDocStrings(*source->ast))
- noErrors = false;
- m_globalContext = make_shared<GlobalContext>();
- NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter);
- for (Source const* source: m_sourceOrder)
- if (!resolver.registerDeclarations(*source->ast))
- return false;
-
- map<string, SourceUnit const*> sourceUnitsByName;
- for (auto& source: m_sources)
- sourceUnitsByName[source.first] = source.second.ast.get();
- for (Source const* source: m_sourceOrder)
- if (!resolver.performImports(*source->ast, sourceUnitsByName))
- return false;
+ try {
+ SyntaxChecker syntaxChecker(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!syntaxChecker.checkSyntax(*source->ast))
+ noErrors = false;
- for (Source const* source: m_sourceOrder)
- for (ASTPointer<ASTNode> const& node: source->ast->nodes())
- if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
- {
- m_globalContext->setCurrentContract(*contract);
- if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false;
- if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false;
- if (!resolver.resolveNamesAndTypes(*contract)) return false;
-
- // Note that we now reference contracts by their fully qualified names, and
- // thus contracts can only conflict if declared in the same source file. This
- // already causes a double-declaration error elsewhere, so we do not report
- // an error here and instead silently drop any additional contracts we find.
-
- if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end())
- m_contracts[contract->fullyQualifiedName()].contract = contract;
- }
+ DocStringAnalyser docStringAnalyser(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!docStringAnalyser.analyseDocStrings(*source->ast))
+ noErrors = false;
- TypeChecker typeChecker(m_evmVersion, m_errorReporter);
- for (Source const* source: m_sourceOrder)
- for (ASTPointer<ASTNode> const& node: source->ast->nodes())
- if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
- if (!typeChecker.checkTypeRequirements(*contract))
- noErrors = false;
+ m_globalContext = make_shared<GlobalContext>();
+ NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!resolver.registerDeclarations(*source->ast))
+ return false;
- if (noErrors)
- {
- PostTypeChecker postTypeChecker(m_errorReporter);
+ map<string, SourceUnit const*> sourceUnitsByName;
+ for (auto& source: m_sources)
+ sourceUnitsByName[source.first] = source.second.ast.get();
for (Source const* source: m_sourceOrder)
- if (!postTypeChecker.check(*source->ast))
- noErrors = false;
- }
+ if (!resolver.performImports(*source->ast, sourceUnitsByName))
+ return false;
- if (noErrors)
- {
- StaticAnalyzer staticAnalyzer(m_errorReporter);
for (Source const* source: m_sourceOrder)
- if (!staticAnalyzer.analyze(*source->ast))
- noErrors = false;
- }
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ {
+ m_globalContext->setCurrentContract(*contract);
+ if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false;
+ if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false;
+ if (!resolver.resolveNamesAndTypes(*contract)) return false;
+
+ // Note that we now reference contracts by their fully qualified names, and
+ // thus contracts can only conflict if declared in the same source file. This
+ // already causes a double-declaration error elsewhere, so we do not report
+ // an error here and instead silently drop any additional contracts we find.
+
+ if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end())
+ m_contracts[contract->fullyQualifiedName()].contract = contract;
+ }
- if (noErrors)
- {
- vector<ASTPointer<ASTNode>> ast;
+ TypeChecker typeChecker(m_evmVersion, m_errorReporter);
for (Source const* source: m_sourceOrder)
- ast.push_back(source->ast);
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ if (!typeChecker.checkTypeRequirements(*contract))
+ noErrors = false;
- if (!ViewPureChecker(ast, m_errorReporter).check())
- noErrors = false;
- }
+ if (noErrors)
+ {
+ PostTypeChecker postTypeChecker(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!postTypeChecker.check(*source->ast))
+ noErrors = false;
+ }
- if (noErrors)
+ if (noErrors)
+ {
+ StaticAnalyzer staticAnalyzer(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ if (!staticAnalyzer.analyze(*source->ast))
+ noErrors = false;
+ }
+
+ if (noErrors)
+ {
+ vector<ASTPointer<ASTNode>> ast;
+ for (Source const* source: m_sourceOrder)
+ ast.push_back(source->ast);
+
+ if (!ViewPureChecker(ast, m_errorReporter).check())
+ noErrors = false;
+ }
+
+ if (noErrors)
+ {
+ SMTChecker smtChecker(m_errorReporter, m_smtQuery);
+ for (Source const* source: m_sourceOrder)
+ smtChecker.analyze(*source->ast);
+ }
+ }
+ catch(FatalError const&)
{
- SMTChecker smtChecker(m_errorReporter, m_smtQuery);
- for (Source const* source: m_sourceOrder)
- smtChecker.analyze(*source->ast);
+ if (m_errorReporter.errors().empty())
+ throw; // Something is weird here, rather throw again.
+ noErrors = false;
}
if (noErrors)
diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h
index 13c4ec94..b68e1f4e 100644
--- a/libsolidity/interface/EVMVersion.h
+++ b/libsolidity/interface/EVMVersion.h
@@ -49,7 +49,7 @@ public:
static boost::optional<EVMVersion> fromString(std::string const& _version)
{
- for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()})
+ for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople()})
if (_version == v.name())
return v;
return {};
diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp
index e6171756..368e25e0 100644
--- a/libsolidity/interface/ErrorReporter.cpp
+++ b/libsolidity/interface/ErrorReporter.cpp
@@ -61,6 +61,9 @@ void ErrorReporter::warning(
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
{
+ if (checkForExcessiveErrors(_type))
+ return;
+
auto err = make_shared<Error>(_type);
*err <<
errinfo_sourceLocation(_location) <<
@@ -71,6 +74,9 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
{
+ if (checkForExcessiveErrors(_type))
+ return;
+
auto err = make_shared<Error>(_type);
*err <<
errinfo_sourceLocation(_location) <<
@@ -80,6 +86,37 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se
m_errorList.push_back(err);
}
+bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
+{
+ if (_type == Error::Type::Warning)
+ {
+ m_warningCount++;
+
+ if (m_warningCount == c_maxWarningsAllowed)
+ {
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err << errinfo_comment("There are more than 256 warnings. Ignoring the rest.");
+ m_errorList.push_back(err);
+ }
+
+ if (m_warningCount >= c_maxWarningsAllowed)
+ return true;
+ }
+ else
+ {
+ m_errorCount++;
+
+ if (m_errorCount > c_maxErrorsAllowed)
+ {
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err << errinfo_comment("There are more than 256 errors. Aborting.");
+ m_errorList.push_back(err);
+ BOOST_THROW_EXCEPTION(FatalError());
+ }
+ }
+
+ return false;
+}
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description)
{
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
index a87db21d..d1a0030f 100644
--- a/libsolidity/interface/ErrorReporter.h
+++ b/libsolidity/interface/ErrorReporter.h
@@ -102,7 +102,16 @@ private:
SourceLocation const& _location = SourceLocation(),
std::string const& _description = std::string());
+ // @returns true if error shouldn't be stored
+ bool checkForExcessiveErrors(Error::Type _type);
+
ErrorList& m_errorList;
+
+ unsigned m_errorCount = 0;
+ unsigned m_warningCount = 0;
+
+ const unsigned c_maxWarningsAllowed = 256;
+ const unsigned c_maxErrorsAllowed = 256;
};
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index 2139395f..a496cc21 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -136,12 +136,19 @@ 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) << (8 * 28))
+ classes.find(u256(1) << 224)
});
+ // lt(calldatasize(), 4) equals to 0 (ignore the shortcut for fallback functions)
+ classes.forceEqual(
+ classes.find(u256(0)),
+ Instruction::LT,
+ Ids{classes.find(Instruction::CALLDATASIZE), classes.find(u256(4))}
+ );
}
PathGasMeter meter(_items, m_evmVersion);
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
index 0409de72..d058d556 100644
--- a/libsolidity/parsing/DocStringParser.cpp
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -119,21 +119,17 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end)
return _end;
}
auto nameEndPos = firstSpaceOrTab(nameStartPos, _end);
- if (nameEndPos == _end)
- {
- appendError("End of param name not found: " + string(nameStartPos, _end));
- return _end;
- }
auto paramName = string(nameStartPos, nameEndPos);
auto descStartPos = skipWhitespace(nameEndPos, _end);
- if (descStartPos == _end)
+ auto nlPos = find(descStartPos, _end, '\n');
+
+ if (descStartPos == nlPos)
{
appendError("No description given for param " + paramName);
return _end;
}
- auto nlPos = find(descStartPos, _end, '\n');
auto paramDesc = string(descStartPos, nlPos);
newTag("param");
m_lastTag->paramName = paramName;
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 8c97f55f..618a0896 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -238,7 +238,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp
Token::Value currentTokenValue = m_scanner->currentToken();
if (currentTokenValue == Token::RBrace)
break;
- else if (currentTokenValue == Token::Function)
+ else if (
+ currentTokenValue == Token::Function ||
+ (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor")
+ )
// This can be a function or a state variable of function type (especially
// complicated to distinguish fallback function from function type state variable)
subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get()));
@@ -283,17 +286,17 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName());
- vector<ASTPointer<Expression>> arguments;
+ unique_ptr<vector<ASTPointer<Expression>>> arguments;
if (m_scanner->currentToken() == Token::LParen)
{
m_scanner->next();
- arguments = parseFunctionCallListArguments();
+ arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments()));
nodeFactory.markEndPosition();
expectToken(Token::RParen);
}
else
nodeFactory.setEndPositionFromNode(name);
- return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
+ return nodeFactory.createNode<InheritanceSpecifier>(name, std::move(arguments));
}
Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
@@ -329,15 +332,31 @@ StateMutability Parser::parseStateMutability(Token::Value _token)
return stateMutability;
}
-Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
+Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(
+ bool _forceEmptyName,
+ bool _allowModifiers,
+ ASTString const* _contractName
+)
{
RecursionGuard recursionGuard(*this);
FunctionHeaderParserResult result;
- expectToken(Token::Function);
- if (_forceEmptyName || m_scanner->currentToken() == Token::LParen)
- result.name = make_shared<ASTString>(); // anonymous function
+
+ result.isConstructor = false;
+
+ if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor")
+ result.isConstructor = true;
+ else if (m_scanner->currentToken() != Token::Function)
+ solAssert(false, "Function or constructor expected.");
+ m_scanner->next();
+
+ if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen)
+ result.name = make_shared<ASTString>();
else
result.name = expectIdentifierToken();
+
+ if (!result.name->empty() && _contractName && *result.name == *_contractName)
+ result.isConstructor = true;
+
VarDeclParserOptions options;
options.allowLocationSpecifier = true;
result.parameters = parseParameterList(options);
@@ -346,12 +365,13 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
Token::Value token = m_scanner->currentToken();
if (_allowModifiers && token == Token::Identifier)
{
- // This can either be a modifier (function declaration) or the name of the
- // variable (function type name plus variable).
- if (
+ // If the name is empty (and this is not a constructor),
+ // then this can either be a modifier (fallback function declaration)
+ // or the name of the state variable (function type name plus variable).
+ if ((result.name->empty() && !result.isConstructor) && (
m_scanner->peekNextToken() == Token::Semicolon ||
m_scanner->peekNextToken() == Token::Assign
- )
+ ))
// Variable declaration, break here.
break;
else
@@ -361,6 +381,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
{
if (result.visibility != Declaration::Visibility::Default)
{
+ // There is the special case of a public state variable of function type.
+ // Detect this and return early.
+ if (
+ (result.visibility == Declaration::Visibility::External || result.visibility == Declaration::Visibility::Internal) &&
+ result.modifiers.empty() &&
+ (result.name->empty() && !result.isConstructor)
+ )
+ break;
parserError(string(
"Visibility already specified as \"" +
Declaration::visibilityToString(result.visibility) +
@@ -407,9 +435,10 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
- FunctionHeaderParserResult header = parseFunctionHeader(false, true);
+ FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName);
if (
+ header.isConstructor ||
!header.modifiers.empty() ||
!header.name->empty() ||
m_scanner->currentToken() == Token::Semicolon ||
@@ -426,12 +455,11 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
}
else
m_scanner->next(); // just consume the ';'
- bool const c_isConstructor = (_contractName && *header.name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(
header.name,
header.visibility,
header.stateMutability,
- c_isConstructor,
+ header.isConstructor,
docstring,
header.parameters,
header.modifiers,
@@ -579,8 +607,10 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier)
{
identifier = make_shared<ASTString>("");
- solAssert(type != nullptr, "");
- nodeFactory.setEndPositionFromNode(type);
+ solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense
+ if (type)
+ nodeFactory.setEndPositionFromNode(type);
+ // if type is null this has already caused an error
}
else
identifier = expectIdentifierToken();
@@ -683,17 +713,17 @@ ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<Identifier> name(parseIdentifier());
- vector<ASTPointer<Expression>> arguments;
+ unique_ptr<vector<ASTPointer<Expression>>> arguments;
if (m_scanner->currentToken() == Token::LParen)
{
m_scanner->next();
- arguments = parseFunctionCallListArguments();
+ arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments()));
nodeFactory.markEndPosition();
expectToken(Token::RParen);
}
else
nodeFactory.setEndPositionFromNode(name);
- return nodeFactory.createNode<ModifierInvocation>(name, arguments);
+ return nodeFactory.createNode<ModifierInvocation>(name, move(arguments));
}
ASTPointer<Identifier> Parser::parseIdentifier()
@@ -776,6 +806,7 @@ ASTPointer<FunctionTypeName> Parser::parseFunctionType()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
FunctionHeaderParserResult header = parseFunctionHeader(true, false);
+ solAssert(!header.isConstructor, "Tried to parse type as constructor.");
return nodeFactory.createNode<FunctionTypeName>(
header.parameters,
header.returnParameters,
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 3f780af9..eb120a61 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -56,6 +56,7 @@ private:
/// This struct is shared for parsing a function header and a function type.
struct FunctionHeaderParserResult
{
+ bool isConstructor;
ASTPointer<ASTString> name;
ASTPointer<ParameterList> parameters;
ASTPointer<ParameterList> returnParameters;
@@ -73,7 +74,11 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
StateMutability parseStateMutability(Token::Value _token);
- FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
+ FunctionHeaderParserResult parseFunctionHeader(
+ bool _forceEmptyName,
+ bool _allowModifiers,
+ ASTString const* _contractName = nullptr
+ );
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp
index 9cec0303..5ce74316 100644
--- a/libsolidity/parsing/Token.cpp
+++ b/libsolidity/parsing/Token.cpp
@@ -53,7 +53,7 @@ namespace solidity
void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second)
{
- solAssert(Token::isElementaryTypeName(_baseType), "");
+ solAssert(Token::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(Token::toString(_baseType)));
if (_baseType == Token::BytesM)
{
solAssert(_second == 0, "There should not be a second size argument to type bytesM.");
diff --git a/scripts/cpp-ethereum/build.sh b/scripts/cpp-ethereum/build.sh
new file mode 100755
index 00000000..23ed1290
--- /dev/null
+++ b/scripts/cpp-ethereum/build.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+
+# Script to build the eth binary from latest develop
+# for ubuntu trusty and ubuntu artful.
+# Requires docker.
+
+set -e
+
+REPO_ROOT="$(dirname "$0")"/../..
+
+for rel in artful trusty
+do
+ docker build -t eth_$rel -f "$REPO_ROOT"/scripts/cpp-ethereum/eth_$rel.docker .
+ tmp_container=$(docker create eth_$rel sh)
+ echo "Built eth ($rel) at $REPO_ROOT/build/eth_$rel"
+ docker cp ${tmp_container}:/build/eth/eth "$REPO_ROOT"/build/eth_$rel
+done \ No newline at end of file
diff --git a/scripts/cpp-ethereum/eth_artful.docker b/scripts/cpp-ethereum/eth_artful.docker
new file mode 100644
index 00000000..7ce9faae
--- /dev/null
+++ b/scripts/cpp-ethereum/eth_artful.docker
@@ -0,0 +1,7 @@
+FROM ubuntu:artful
+
+RUN apt update
+RUN apt -y install libleveldb-dev cmake g++ git
+RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1
+RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off
+RUN cd /build && make eth
diff --git a/scripts/cpp-ethereum/eth_trusty.docker b/scripts/cpp-ethereum/eth_trusty.docker
new file mode 100644
index 00000000..5cfb59f7
--- /dev/null
+++ b/scripts/cpp-ethereum/eth_trusty.docker
@@ -0,0 +1,13 @@
+FROM ubuntu:trusty
+
+RUN apt-get update
+RUN apt-get -y install software-properties-common python-software-properties
+RUN add-apt-repository ppa:ubuntu-toolchain-r/test
+RUN apt-get update
+RUN apt-get -y install gcc libleveldb-dev git curl make gcc-7 g++-7
+RUN ln -sf /usr/bin/gcc-7 /usr/bin/gcc
+RUN ln -sf /usr/bin/g++-7 /usr/bin/g++
+RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1
+RUN ./cpp-ethereum/scripts/install_cmake.sh
+RUN mkdir /build && cd /build && ~/.local/bin/cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off
+RUN cd /build && make eth
diff --git a/scripts/extract_test_cases.py b/scripts/extract_test_cases.py
new file mode 100755
index 00000000..07ef9a96
--- /dev/null
+++ b/scripts/extract_test_cases.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+#
+# This script reads C++ or RST source files and writes all
+# multi-line strings into individual files.
+# This can be used to extract the Solidity test cases
+# into files for e.g. fuzz testing as
+# scripts/isolate_tests.py test/libsolidity/*
+
+import sys
+import re
+import os
+import hashlib
+from os.path import join
+
+def extract_test_cases(path):
+ lines = open(path, 'rb').read().splitlines()
+
+ inside = False
+ delimiter = ''
+ test = ''
+
+ ctr = 1
+ test_name = ''
+
+ for l in lines:
+ if inside:
+ if l.strip().endswith(')' + delimiter + '";'):
+ open('%03d_%s.sol' % (ctr, test_name), 'wb').write(test)
+ ctr += 1
+ inside = False
+ test = ''
+ else:
+ l = re.sub('^\t\t', '', l)
+ l = l.replace('\t', ' ')
+ test += l + '\n'
+ else:
+ m = re.search(r'BOOST_AUTO_TEST_CASE\(([^(]*)\)', l.strip())
+ if m:
+ test_name = m.group(1)
+ m = re.search(r'R"([^(]*)\($', l.strip())
+ if m:
+ inside = True
+ delimiter = m.group(1)
+
+
+if __name__ == '__main__':
+ path = sys.argv[1]
+ extract_test_cases(path)
+
diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py
index cfaef210..5bf577d3 100755
--- a/scripts/isolate_tests.py
+++ b/scripts/isolate_tests.py
@@ -86,10 +86,15 @@ if __name__ == '__main__':
for root, subdirs, files in os.walk(path):
if '_build' in subdirs:
subdirs.remove('_build')
+ if 'compilationTests' in subdirs:
+ subdirs.remove('compilationTests')
for f in files:
path = join(root, f)
if docs:
cases = extract_docs_cases(path)
else:
- cases = extract_test_cases(path)
+ if f.endswith(".sol"):
+ cases = [open(path, "r").read()]
+ else:
+ cases = extract_test_cases(path)
write_cases(cases)
diff --git a/scripts/isoltest.sh b/scripts/isoltest.sh
new file mode 100755
index 00000000..8aed0b3a
--- /dev/null
+++ b/scripts/isoltest.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -e
+
+REPO_ROOT="$(dirname "$0")"/..
+exec ${REPO_ROOT}/build/test/tools/isoltest --testpath ${REPO_ROOT}/test
diff --git a/scripts/soltest.sh b/scripts/soltest.sh
new file mode 100755
index 00000000..00f484a1
--- /dev/null
+++ b/scripts/soltest.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+set -e
+
+REPO_ROOT="$(dirname "$0")"/..
+USE_DEBUGGER=0
+DEBUGGER="gdb --args"
+BOOST_OPTIONS=
+SOLTEST_OPTIONS=
+
+while [ $# -gt 0 ]
+do
+ case "$1" in
+ --debugger)
+ shift
+ DEBUGGER="$1"
+ USE_DEBUGGER=1
+ ;;
+ --debug)
+ USE_DEBUGGER=1
+ ;;
+ --boost-options)
+ shift
+ BOOST_OPTIONS="${BOOST_OPTIONS} $1"
+ ;;
+ -t)
+ shift
+ BOOST_OPTIONS="${BOOST_OPTIONS} -t $1"
+ ;;
+ --show-progress | -p)
+ BOOST_OPTIONS="${BOOST_OPTIONS} $1"
+ ;;
+ *)
+ SOLTEST_OPTIONS="${SOLTEST_OPTIONS} $1"
+ ;;
+ esac
+ shift
+done
+if [ "$USE_DEBUGGER" -ne "0" ]; then
+ DEBUG_PREFIX=${DEBUGGER}
+fi
+
+exec ${DEBUG_PREFIX} ${REPO_ROOT}/build/test/soltest ${BOOST_OPTIONS} -- --testpath ${REPO_ROOT}/test ${SOLTEST_OPTIONS}
diff --git a/scripts/tests.sh b/scripts/tests.sh
index bf4ee3d9..425a4ff4 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -61,13 +61,13 @@ function download_eth()
mkdir -p /tmp/test
if grep -i trusty /etc/lsb-release >/dev/null 2>&1
then
- # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06
- ETH_BINARY=eth_byzantium_trusty
- ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b"
+ # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05
+ ETH_BINARY=eth_2018-04-05_trusty
+ ETH_HASH="1e5e178b005e5b51f9d347df4452875ba9b53cc6"
else
- # built from ?? at 2018-02-13 ?
- ETH_BINARY=eth_byzantium_artful
- ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
+ # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05
+ ETH_BINARY=eth_2018-04-05_artful
+ ETH_HASH="eb2d0df022753bb2b442ba73e565a9babf6828d6"
fi
wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY
test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth"
@@ -94,16 +94,23 @@ download_eth
ETH_PID=$(run_eth /tmp/test)
progress="--show-progress"
-if [ "$CI" ]
+if [ "$CIRCLECI" ]
then
progress=""
fi
+EVM_VERSIONS="homestead byzantium"
+
+if [ "$CIRCLECI" ] || [ -z "$CI" ]
+then
+EVM_VERSIONS+=" constantinople"
+fi
+
# And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer
# and homestead / byzantium VM, # pointing to that IPC endpoint.
for optimize in "" "--optimize"
do
- for vm in homestead byzantium
+ for vm in $EVM_VERSIONS
do
echo "--> Running tests using "$optimize" --evm-version "$vm"..."
log=""
@@ -116,7 +123,7 @@ do
log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt
fi
fi
- "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc
+ "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc
done
done
diff --git a/scripts/uniqueErrors.sh b/scripts/uniqueErrors.sh
index eee1df90..fa2c7b4c 100755
--- a/scripts/uniqueErrors.sh
+++ b/scripts/uniqueErrors.sh
@@ -9,6 +9,6 @@ do
echo -n $x " # "
# This subshell is a workaround to prevent the shell from printing
# "Aborted"
- ("$REPO"/build/test/solfuzzer < "$x" || true) 2>&1 | head -n 1
+ ("$REPO"/build/test/tools/solfuzzer < "$x" || true) 2>&1 | head -n 1
done
) | sort -u -t'#' -k 2
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index d3d234c3..bd5e2eb1 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -116,6 +116,7 @@ static string const g_strStandardJSON = "standard-json";
static string const g_strStrictAssembly = "strict-assembly";
static string const g_strPrettyJson = "pretty-json";
static string const g_strVersion = "version";
+static string const g_strIgnoreMissingFiles = "ignore-missing";
static string const g_argAbi = g_strAbi;
static string const g_argPrettyJson = g_strPrettyJson;
@@ -152,6 +153,7 @@ static string const g_argStandardJSON = g_strStandardJSON;
static string const g_argStrictAssembly = g_strStrictAssembly;
static string const g_argVersion = g_strVersion;
static string const g_stdinFileName = g_stdinFileNameStr;
+static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles;
/// Possible arguments to for --combined-json
static set<string> const g_combinedJsonArgs
@@ -398,8 +400,9 @@ void CommandLineInterface::handleGasEstimation(string const& _contract)
}
}
-void CommandLineInterface::readInputFilesAndConfigureRemappings()
+bool CommandLineInterface::readInputFilesAndConfigureRemappings()
{
+ bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles);
bool addStdin = false;
if (!m_args.count(g_argInputFile))
addStdin = true;
@@ -416,13 +419,27 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings()
auto infile = boost::filesystem::path(path);
if (!boost::filesystem::exists(infile))
{
- cerr << "Skipping non-existent input file \"" << infile << "\"" << endl;
+ if (!ignoreMissing)
+ {
+ cerr << "\"" << infile << "\" is not found" << endl;
+ return false;
+ }
+ else
+ cerr << "\"" << infile << "\" is not found. Skipping." << endl;
+
continue;
}
if (!boost::filesystem::is_regular_file(infile))
{
- cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl;
+ if (!ignoreMissing)
+ {
+ cerr << "\"" << infile << "\" is not a valid file" << endl;
+ return false;
+ }
+ else
+ cerr << "\"" << infile << "\" is not a valid file. Skipping." << endl;
+
continue;
}
@@ -433,6 +450,8 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings()
}
if (addStdin)
m_sourceCodes[g_stdinFileName] = dev::readStandardInput();
+
+ return true;
}
bool CommandLineInterface::parseLibraryOption(string const& _input)
@@ -599,7 +618,8 @@ Allowed options)",
g_argAllowPaths.c_str(),
po::value<string>()->value_name("path(s)"),
"Allow a given path for imports. A list of paths can be supplied by separating them with a comma."
- );
+ )
+ (g_argIgnoreMissingFiles.c_str(), "Ignore missing files.");
po::options_description outputComponents("Output Components");
outputComponents.add_options()
(g_argAst.c_str(), "AST of all source files.")
@@ -616,7 +636,7 @@ Allowed options)",
(g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.")
(g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.")
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
- (g_argFormal.c_str(), "Translated source suitable for formal analysis.");
+ (g_argFormal.c_str(), "Translated source suitable for formal analysis. (Deprecated)");
desc.add(outputComponents);
po::options_description allOptions = desc;
@@ -680,7 +700,7 @@ bool CommandLineInterface::processInput()
try
{
auto path = boost::filesystem::path(_path);
- auto canonicalPath = boost::filesystem::canonical(path);
+ auto canonicalPath = weaklyCanonicalFilesystemPath(path);
bool isAllowed = false;
for (auto const& allowedDir: m_allowedDirectories)
{
@@ -696,16 +716,16 @@ bool CommandLineInterface::processInput()
}
if (!isAllowed)
return ReadCallback::Result{false, "File outside of allowed directories."};
- else if (!boost::filesystem::exists(path))
+
+ if (!boost::filesystem::exists(canonicalPath))
return ReadCallback::Result{false, "File not found."};
- else if (!boost::filesystem::is_regular_file(canonicalPath))
+
+ if (!boost::filesystem::is_regular_file(canonicalPath))
return ReadCallback::Result{false, "Not a valid file."};
- else
- {
- auto contents = dev::readFileAsString(canonicalPath.string());
- m_sourceCodes[path.string()] = contents;
- return ReadCallback::Result{true, contents};
- }
+
+ auto contents = dev::readFileAsString(canonicalPath.string());
+ m_sourceCodes[path.string()] = contents;
+ return ReadCallback::Result{true, contents};
}
catch (Exception const& _exception)
{
@@ -741,7 +761,8 @@ bool CommandLineInterface::processInput()
return true;
}
- readInputFilesAndConfigureRemappings();
+ if (!readInputFilesAndConfigureRemappings())
+ return false;
if (m_args.count(g_argLibraries))
for (string const& library: m_args[g_argLibraries].as<vector<string>>())
diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h
index 303023fc..45ec1eb5 100644
--- a/solc/CommandLineInterface.h
+++ b/solc/CommandLineInterface.h
@@ -72,7 +72,7 @@ private:
void handleFormal();
/// Fills @a m_sourceCodes initially and @a m_redirects.
- void readInputFilesAndConfigureRemappings();
+ bool readInputFilesAndConfigureRemappings();
/// Tries to read from the file @a _input or interprets _input literally if that fails.
/// It then tries to parse the contents and appends to m_libraries.
bool parseLibraryOption(std::string const& _input);
diff --git a/std/StandardToken.sol b/std/StandardToken.sol
index 1b218d67..ca0658f2 100644
--- a/std/StandardToken.sol
+++ b/std/StandardToken.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.0;
+pragma solidity ^0.4.22;
import "./Token.sol";
@@ -8,7 +8,7 @@ contract StandardToken is Token {
mapping (address =>
mapping (address => uint256)) m_allowance;
- function StandardToken(address _initialOwner, uint256 _supply) public {
+ constructor(address _initialOwner, uint256 _supply) public {
supply = _supply;
balance[_initialOwner] = _supply;
}
diff --git a/std/owned.sol b/std/owned.sol
index ee9860d3..75007f3e 100644
--- a/std/owned.sol
+++ b/std/owned.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.0;
+pragma solidity ^0.4.22;
contract owned {
address owner;
@@ -9,7 +9,7 @@ contract owned {
}
}
- function owned() public {
+ constructor() public {
owner = msg.sender;
}
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f36ad4c5..522856cc 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,9 +1,27 @@
-file(GLOB_RECURSE sources "*.cpp")
-list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp")
-file(GLOB_RECURSE headers "*.h")
+file(GLOB sources "*.cpp")
+file(GLOB headers "*.h")
-add_executable(soltest ${sources} ${headers})
+file(GLOB contracts_sources "contracts/*.cpp")
+file(GLOB contracts_headers "contracts/*.h")
+file(GLOB libdevcore_sources "libdevcore/*.cpp")
+file(GLOB libdevcore_headers "libdevcore/*.h")
+file(GLOB libevmasm_sources "libevmasm/*.cpp")
+file(GLOB libevmasm_headers "libevmasm/*.h")
+file(GLOB libjulia_sources "libjulia/*.cpp")
+file(GLOB libjulia_headers "libjulia/*.h")
+file(GLOB liblll_sources "liblll/*.cpp")
+file(GLOB liblll_headers "liblll/*.h")
+file(GLOB libsolidity_sources "libsolidity/*.cpp")
+file(GLOB libsolidity_headers "libsolidity/*.h")
+
+add_executable(soltest ${sources} ${headers}
+ ${contracts_sources} ${contracts_headers}
+ ${libdevcore_sources} ${libdevcore_headers}
+ ${libevmasm_sources} ${libevmasm_headers}
+ ${libjulia_sources} ${libjulia_headers}
+ ${liblll_sources} ${liblll_headers}
+ ${libsolidity_sources} ${libsolidity_headers}
+)
target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
-add_executable(solfuzzer fuzzer.cpp)
-target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES})
+add_subdirectory(tools)
diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h
index a7971b81..ee8da322 100644
--- a/test/ExecutionFramework.h
+++ b/test/ExecutionFramework.h
@@ -22,7 +22,7 @@
#pragma once
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <test/RPCSession.h>
#include <libsolidity/interface/EVMVersion.h>
diff --git a/test/TestHelper.cpp b/test/Options.cpp
index e0d4423d..ff4a7c98 100644
--- a/test/TestHelper.cpp
+++ b/test/Options.cpp
@@ -19,9 +19,10 @@
* @date 2014
*/
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/interface/EVMVersion.h>
+#include <libsolidity/interface/Exceptions.h>
#include <boost/test/framework.hpp>
@@ -43,6 +44,11 @@ Options::Options()
ipcPath = suite.argv[i + 1];
i++;
}
+ else if (string(suite.argv[i]) == "--testpath" && i + 1 < suite.argc)
+ {
+ testPath = suite.argv[i + 1];
+ i++;
+ }
else if (string(suite.argv[i]) == "--optimize")
optimize = true;
else if (string(suite.argv[i]) == "--evm-version")
@@ -60,6 +66,23 @@ Options::Options()
if (!disableIPC && ipcPath.empty())
if (auto path = getenv("ETH_TEST_IPC"))
ipcPath = path;
+
+ if (testPath.empty())
+ if (auto path = getenv("ETH_TEST_PATH"))
+ testPath = path;
+}
+
+void Options::validate() const
+{
+ solAssert(
+ !dev::test::Options::get().testPath.empty(),
+ "No test path specified. The --testpath argument is required."
+ );
+ if (!disableIPC)
+ solAssert(
+ !dev::test::Options::get().ipcPath.empty(),
+ "No ipc path specified. The --ipcpath argument is required, unless --no-ipc is used."
+ );
}
dev::solidity::EVMVersion Options::evmVersion() const
diff --git a/test/TestHelper.h b/test/Options.h
index 8c2eec36..9bc69876 100644
--- a/test/TestHelper.h
+++ b/test/Options.h
@@ -35,11 +35,13 @@ namespace test
struct Options: boost::noncopyable
{
std::string ipcPath;
+ boost::filesystem::path testPath;
bool showMessages = false;
bool optimize = false;
bool disableIPC = false;
bool disableSMT = false;
+ void validate() const;
solidity::EVMVersion evmVersion() const;
static Options const& get();
diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp
index 54871057..f4eae865 100644
--- a/test/RPCSession.cpp
+++ b/test/RPCSession.cpp
@@ -21,7 +21,7 @@
#include <test/RPCSession.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/interface/EVMVersion.h>
@@ -226,6 +226,8 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts)
forks += "\"EIP158ForkBlock\": \"0x00\",\n";
if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium())
forks += "\"byzantiumForkBlock\": \"0x00\",\n";
+ if (test::Options::get().evmVersion() >= solidity::EVMVersion::constantinople())
+ forks += "\"constantinopleForkBlock\": \"0x00\",\n";
static string const c_configString = R"(
{
"sealEngine": "NoProof",
@@ -337,7 +339,9 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const&
BOOST_TEST_MESSAGE("Reply: " + reply);
Json::Value result;
- BOOST_REQUIRE(jsonParseStrict(reply, result));
+ string errorMsg;
+ if (!jsonParseStrict(reply, result, &errorMsg))
+ BOOST_REQUIRE_MESSAGE(false, errorMsg);
if (result.isMember("error"))
{
diff --git a/test/boostTest.cpp b/test/boostTest.cpp
index a3cc51c5..f16973b5 100644
--- a/test/boostTest.cpp
+++ b/test/boostTest.cpp
@@ -35,7 +35,8 @@
#pragma GCC diagnostic pop
-#include <test/TestHelper.h>
+#include <test/Options.h>
+#include <test/libsolidity/SyntaxTest.h>
using namespace boost::unit_test;
@@ -54,6 +55,12 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
{
master_test_suite_t& master = framework::master_test_suite();
master.p_name.value = "SolidityTests";
+ dev::test::Options::get().validate();
+ solAssert(dev::solidity::test::SyntaxTest::registerTests(
+ master,
+ dev::test::Options::get().testPath / "libsolidity",
+ "syntaxTests"
+ ) > 0, "no syntax tests found");
if (dev::test::Options::get().disableIPC)
{
for (auto suite: {
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index e86e0ad4..1137c7b0 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -32,7 +32,7 @@ REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
echo $REPO_ROOT
SOLC="$REPO_ROOT/build/solc/solc"
-FULLARGS="--optimize --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
+FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc"
echo "Checking that the bug list is up to date..."
"$REPO_ROOT"/scripts/update_bugs_by_version.py
@@ -170,14 +170,14 @@ TMPDIR=$(mktemp -d)
for f in *.sol
do
set +e
- "$REPO_ROOT"/build/test/solfuzzer --quiet < "$f"
+ "$REPO_ROOT"/build/test/tools/solfuzzer --quiet < "$f"
if [ $? -ne 0 ]; then
printError "Fuzzer failed on:"
cat "$f"
exit 1
fi
- "$REPO_ROOT"/build/test/solfuzzer --without-optimizer --quiet < "$f"
+ "$REPO_ROOT"/build/test/tools/solfuzzer --without-optimizer --quiet < "$f"
if [ $? -ne 0 ]; then
printError "Fuzzer (without optimizer) failed on:"
cat "$f"
diff --git a/test/libdevcore/Checksum.cpp b/test/libdevcore/Checksum.cpp
index 4eedbd99..95066b69 100644
--- a/test/libdevcore/Checksum.cpp
+++ b/test/libdevcore/Checksum.cpp
@@ -22,7 +22,7 @@
#include <libdevcore/Exceptions.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libdevcore/IndentedWriter.cpp b/test/libdevcore/IndentedWriter.cpp
index a694aa1b..916c99d0 100644
--- a/test/libdevcore/IndentedWriter.cpp
+++ b/test/libdevcore/IndentedWriter.cpp
@@ -20,7 +20,7 @@
#include <libdevcore/IndentedWriter.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libdevcore/JSON.cpp b/test/libdevcore/JSON.cpp
index 39d71b42..39d958f5 100644
--- a/test/libdevcore/JSON.cpp
+++ b/test/libdevcore/JSON.cpp
@@ -21,7 +21,7 @@
#include <libdevcore/JSON.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libdevcore/StringUtils.cpp b/test/libdevcore/StringUtils.cpp
index 597457cc..9ee93d02 100644
--- a/test/libdevcore/StringUtils.cpp
+++ b/test/libdevcore/StringUtils.cpp
@@ -20,7 +20,7 @@
#include <libdevcore/StringUtils.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libdevcore/SwarmHash.cpp b/test/libdevcore/SwarmHash.cpp
index 1ed1da18..913586f8 100644
--- a/test/libdevcore/SwarmHash.cpp
+++ b/test/libdevcore/SwarmHash.cpp
@@ -20,7 +20,7 @@
#include <libdevcore/SwarmHash.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libdevcore/UTF8.cpp b/test/libdevcore/UTF8.cpp
index 719ada72..6de4570f 100644
--- a/test/libdevcore/UTF8.cpp
+++ b/test/libdevcore/UTF8.cpp
@@ -21,7 +21,7 @@
#include <libdevcore/CommonData.h>
#include <libdevcore/UTF8.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libdevcore/Whiskers.cpp b/test/libdevcore/Whiskers.cpp
index 84149173..b12acdd7 100644
--- a/test/libdevcore/Whiskers.cpp
+++ b/test/libdevcore/Whiskers.cpp
@@ -20,7 +20,7 @@
#include <libdevcore/Whiskers.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp
index e6abcb53..089be45d 100644
--- a/test/libevmasm/Optimiser.cpp
+++ b/test/libevmasm/Optimiser.cpp
@@ -20,7 +20,7 @@
* Tests for the Solidity optimizer.
*/
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/PeepholeOptimiser.h>
@@ -69,8 +69,9 @@ namespace
{
AssemblyItems input = addDummyLocations(_input);
+ bool usesMsize = (find(_input.begin(), _input.end(), AssemblyItem{Instruction::MSIZE}) != _input.end());
eth::CommonSubexpressionEliminator cse(_state);
- BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
+ BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end());
AssemblyItems output = cse.getOptimizedItems();
for (AssemblyItem const& item: output)
@@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1,
Instruction::DIV, u256(0xff), Instruction::AND
};
- BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
+ BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.end());
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK(!output.empty());
}
@@ -857,6 +858,115 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize)
BOOST_CHECK(items.empty());
}
+BOOST_AUTO_TEST_CASE(peephole_commutative_swap1)
+{
+ vector<Instruction> ops{
+ Instruction::ADD,
+ Instruction::MUL,
+ Instruction::EQ,
+ Instruction::AND,
+ Instruction::OR,
+ Instruction::XOR
+ };
+ for (Instruction const op: ops)
+ {
+ AssemblyItems items{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op,
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(1),
+ u256(2),
+ op,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+ }
+}
+
+BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1)
+{
+ // NOTE: not comprehensive
+ vector<Instruction> ops{
+ Instruction::SUB,
+ Instruction::DIV,
+ Instruction::SDIV,
+ Instruction::MOD,
+ Instruction::SMOD,
+ Instruction::EXP
+ };
+ for (Instruction const op: ops)
+ {
+ AssemblyItems items{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op,
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(!peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+ }
+}
+
+BOOST_AUTO_TEST_CASE(peephole_swap_comparison)
+{
+ map<Instruction, Instruction> swappableOps{
+ { Instruction::LT, Instruction::GT },
+ { Instruction::GT, Instruction::LT },
+ { Instruction::SLT, Instruction::SGT },
+ { Instruction::SGT, Instruction::SLT }
+ };
+
+ for (auto const& op: swappableOps)
+ {
+ AssemblyItems items{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op.first,
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(1),
+ u256(2),
+ op.second,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+ }
+}
+
BOOST_AUTO_TEST_CASE(jumpdest_removal)
{
AssemblyItems items{
diff --git a/test/libevmasm/SourceLocation.cpp b/test/libevmasm/SourceLocation.cpp
index 6889b3e6..764da3cd 100644
--- a/test/libevmasm/SourceLocation.cpp
+++ b/test/libevmasm/SourceLocation.cpp
@@ -22,7 +22,7 @@
#include <libevmasm/SourceLocation.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
namespace dev
{
diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp
index 41f5d320..24519b01 100644
--- a/test/libjulia/Common.cpp
+++ b/test/libjulia/Common.cpp
@@ -21,7 +21,7 @@
#include <test/libjulia/Common.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libjulia/optimiser/Disambiguator.h>
diff --git a/test/libjulia/CommonSubexpression.cpp b/test/libjulia/CommonSubexpression.cpp
new file mode 100644
index 00000000..8a575c48
--- /dev/null
+++ b/test/libjulia/CommonSubexpression.cpp
@@ -0,0 +1,102 @@
+/*
+ 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/>.
+*/
+/**
+ * Unit tests for the common subexpression eliminator optimizer stage.
+ */
+
+#include <test/libjulia/Common.h>
+
+#include <libjulia/optimiser/CommonSubexpressionEliminator.h>
+
+#include <libsolidity/inlineasm/AsmPrinter.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <boost/range/adaptors.hpp>
+#include <boost/algorithm/string/join.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace dev::julia;
+using namespace dev::julia::test;
+using namespace dev::solidity;
+
+
+#define CHECK(_original, _expectation)\
+do\
+{\
+ assembly::AsmPrinter p;\
+ Block b = disambiguate(_original, false);\
+ (CommonSubexpressionEliminator{})(b);\
+ string result = p(b);\
+ BOOST_CHECK_EQUAL(result, format(_expectation, false));\
+}\
+while(false)
+
+BOOST_AUTO_TEST_SUITE(IuliaCSE)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ CHECK("{ }", "{ }");
+}
+
+BOOST_AUTO_TEST_CASE(trivial)
+{
+ CHECK(
+ "{ let a := mul(1, codesize()) let b := mul(1, codesize()) }",
+ "{ let a := mul(1, codesize()) let b := a }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(non_movable_instr)
+{
+ CHECK(
+ "{ let a := mload(1) let b := mload(1) }",
+ "{ let a := mload(1) let b := mload(1) }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(non_movable_instr2)
+{
+ CHECK(
+ "{ let a := gas() let b := gas() }",
+ "{ let a := gas() let b := gas() }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(branches_if)
+{
+ CHECK(
+ "{ let b := 1 if b { b := 1 } let c := 1 }",
+ "{ let b := 1 if b { b := b } let c := 1 }"
+ );
+}
+
+BOOST_AUTO_TEST_CASE(branches_for)
+{
+ CHECK(
+ "{ let a := 1 let b := codesize()"
+ "for { } lt(1, codesize()) { mstore(1, codesize()) a := add(a, codesize()) }"
+ "{ mstore(1, codesize()) } mstore(1, codesize()) }",
+
+ "{ let a := 1 let b := codesize()"
+ "for { } lt(1, b) { mstore(1, b) a := add(a, b) }"
+ "{ mstore(1, b) } mstore(1, b) }"
+ );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp
index df905dd6..9d66658e 100644
--- a/test/libjulia/Parser.cpp
+++ b/test/libjulia/Parser.cpp
@@ -19,7 +19,7 @@
* Unit tests for parsing Julia.
*/
-#include "../TestHelper.h"
+#include <test/Options.h>
#include <test/libsolidity/ErrorCheck.h>
diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp
index 6c6eae3f..ebdea185 100644
--- a/test/liblll/Compiler.cpp
+++ b/test/liblll/Compiler.cpp
@@ -20,7 +20,7 @@
* Unit tests for the LLL compiler.
*/
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libdevcore/FixedHash.h>
diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp
index e5e70cf8..fd8099f2 100644
--- a/test/liblll/EndToEndTest.cpp
+++ b/test/liblll/EndToEndTest.cpp
@@ -21,7 +21,7 @@
*/
#include <test/liblll/ExecutionFramework.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <boost/test/unit_test.hpp>
diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp
index 15c04b37..beb7b5af 100644
--- a/test/libsolidity/ABIDecoderTests.cpp
+++ b/test/libsolidity/ABIDecoderTests.cpp
@@ -786,6 +786,89 @@ BOOST_AUTO_TEST_CASE(complex_struct)
}
+BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_simple)
+{
+ if (m_evmVersion == EVMVersion::homestead())
+ return;
+
+ string sourceCode = R"(
+ contract C {
+ function dyn() public returns (bytes) {
+ return "1234567890123456789012345678901234567890";
+ }
+ function f() public returns (bytes) {
+ return this.dyn();
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0x20, 40, string("1234567890123456789012345678901234567890")));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced)
+{
+ if (m_evmVersion == EVMVersion::homestead())
+ return;
+
+ string sourceCode = R"(
+ contract C {
+ function dyn() public returns (bytes a, uint b, bytes20[] c, uint d) {
+ a = "1234567890123456789012345678901234567890";
+ b = uint(-1);
+ c = new bytes20[](4);
+ c[0] = bytes20(1234);
+ c[3] = bytes20(6789);
+ d = 0x1234;
+ }
+ function f() public returns (bytes, uint, bytes20[], uint) {
+ return this.dyn();
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(
+ 0x80, u256(-1), 0xe0, 0x1234,
+ 40, string("1234567890123456789012345678901234567890"),
+ 4, u256(1234) << (8 * (32 - 20)), 0, 0, u256(6789) << (8 * (32 - 20))
+ ));
+ )
+}
+
+BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_out_of_range)
+{
+ string sourceCode = R"(
+ contract C {
+ function dyn(uint x) public returns (bytes a) {
+ assembly {
+ mstore(0, 0x20)
+ mstore(0x20, 0x21)
+ return(0, x)
+ }
+ }
+ function f(uint x) public returns (bool) {
+ this.dyn(x);
+ return true;
+ }
+ }
+ )";
+ BOTH_ENCODERS(
+ compileAndRun(sourceCode, 0, "C");
+ if (m_evmVersion == EVMVersion::homestead())
+ {
+ ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs(true));
+ ABI_CHECK(callContractFunction("f(uint256)", 0x7f), encodeArgs(true));
+ }
+ else
+ {
+ ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs());
+ ABI_CHECK(callContractFunction("f(uint256)", 0x61), encodeArgs(true));
+ }
+ ABI_CHECK(callContractFunction("f(uint256)", 0x80), encodeArgs(true));
+ )
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp
index 9bf60b64..5d5b14e8 100644
--- a/test/libsolidity/ASTJSON.cpp
+++ b/test/libsolidity/ASTJSON.cpp
@@ -20,7 +20,7 @@
* Tests for the json ast output.
*/
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/CompilerStack.h>
@@ -41,207 +41,62 @@ namespace test
BOOST_AUTO_TEST_SUITE(SolidityASTJSON)
-BOOST_AUTO_TEST_CASE(smoke_test)
+BOOST_AUTO_TEST_CASE(short_type_name)
{
CompilerStack c;
- c.addSource("a", "contract C {}");
+ c.addSource("a", "contract c { function f() { uint[] memory x; } }");
c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit");
+ Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
+ Json::Value varDecl = astJson["nodes"][0]["nodes"][0]["body"]["statements"][0]["declarations"][0];
+ BOOST_CHECK_EQUAL(varDecl["storageLocation"], "memory");
+ BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeIdentifier"], "t_array$_t_uint256_$dyn_memory_ptr");
+ BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeString"], "uint256[]");
}
-BOOST_AUTO_TEST_CASE(source_location)
+BOOST_AUTO_TEST_CASE(short_type_name_ref)
{
CompilerStack c;
- c.addSource("a", "contract C { function f() { var x = 2; x++; } }");
+ c.addSource("a", "contract c { function f() { uint[][] memory rows; } }");
c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit");
- BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition");
- BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition");
- BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["src"], "13:32:1");
+ Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
+ Json::Value varDecl = astJson["nodes"][0]["nodes"][0]["body"]["statements"][0]["declarations"][0];
+ BOOST_CHECK_EQUAL(varDecl["storageLocation"], "memory");
+ BOOST_CHECK_EQUAL(varDecl["typeName"]["typeDescriptions"]["typeIdentifier"], "t_array$_t_array$_t_uint256_$dyn_storage_$dyn_storage_ptr");
+ BOOST_CHECK_EQUAL(varDecl["typeName"]["typeDescriptions"]["typeString"], "uint256[][]");
}
-BOOST_AUTO_TEST_CASE(inheritance_specifier)
+BOOST_AUTO_TEST_CASE(long_type_name_binary_operation)
{
CompilerStack c;
- c.addSource("a", "contract C1 {} contract C2 is C1 {}");
+ c.addSource("a", "contract c { function f() public { uint a = 2 + 3; } }");
c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2");
- BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier");
- BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1");
- BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["name"], "UserDefinedTypeName");
- BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["attributes"]["name"], "C1");
+ Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
+ Json::Value varDecl = astJson["nodes"][0]["nodes"][0]["body"]["statements"][0]["initialValue"]["commonType"];
+ BOOST_CHECK_EQUAL(varDecl["typeIdentifier"], "t_rational_5_by_1");
+ BOOST_CHECK_EQUAL(varDecl["typeString"], "int_const 5");
}
-BOOST_AUTO_TEST_CASE(using_for_directive)
+BOOST_AUTO_TEST_CASE(long_type_name_identifier)
{
CompilerStack c;
- c.addSource("a", "library L {} contract C { using L for uint; }");
+ c.addSource("a", "contract c { uint[] a; function f() public { uint[] b = a; } }");
c.setEVMVersion(dev::test::Options::get().evmVersion());
c.parseAndAnalyze();
map<string, unsigned> sourceIndices;
sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value usingFor = astJson["children"][1]["children"][0];
- BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective");
- BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1");
- BOOST_CHECK_EQUAL(usingFor["children"][0]["name"], "UserDefinedTypeName");
- BOOST_CHECK_EQUAL(usingFor["children"][0]["attributes"]["name"], "L");
- BOOST_CHECK_EQUAL(usingFor["children"][1]["name"], "ElementaryTypeName");
- BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint");
-}
-
-BOOST_AUTO_TEST_CASE(enum_value)
-{
- CompilerStack c;
- c.addSource("a", "contract C { enum E { A, B } }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value enumDefinition = astJson["children"][0]["children"][0];
- BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue");
- BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A");
- BOOST_CHECK_EQUAL(enumDefinition["children"][0]["src"], "22:1:1");
- BOOST_CHECK_EQUAL(enumDefinition["children"][1]["name"], "EnumValue");
- BOOST_CHECK_EQUAL(enumDefinition["children"][1]["attributes"]["name"], "B");
- BOOST_CHECK_EQUAL(enumDefinition["children"][1]["src"], "25:1:1");
-}
-
-BOOST_AUTO_TEST_CASE(modifier_definition)
-{
- CompilerStack c;
- c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value modifier = astJson["children"][0]["children"][0];
- BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition");
- BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M");
- BOOST_CHECK_EQUAL(modifier["src"], "13:25:1");
-}
-
-BOOST_AUTO_TEST_CASE(modifier_invocation)
-{
- CompilerStack c;
- c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value modifier = astJson["children"][0]["children"][1]["children"][2];
- BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation");
- BOOST_CHECK_EQUAL(modifier["src"], "52:4:1");
- BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["type"], "modifier (uint256)");
- BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["value"], "M");
- BOOST_CHECK_EQUAL(modifier["children"][1]["attributes"]["value"], "1");
-}
-
-BOOST_AUTO_TEST_CASE(event_definition)
-{
- CompilerStack c;
- c.addSource("a", "contract C { event E(); }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value event = astJson["children"][0]["children"][0];
- BOOST_CHECK_EQUAL(event["name"], "EventDefinition");
- BOOST_CHECK_EQUAL(event["attributes"]["name"], "E");
- BOOST_CHECK_EQUAL(event["src"], "13:10:1");
-}
-
-BOOST_AUTO_TEST_CASE(array_type_name)
-{
- CompilerStack c;
- c.addSource("a", "contract C { uint[] i; }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value array = astJson["children"][0]["children"][0]["children"][0];
- BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName");
- BOOST_CHECK_EQUAL(array["src"], "13:6:1");
-}
-
-BOOST_AUTO_TEST_CASE(placeholder_statement)
-{
- CompilerStack c;
- c.addSource("a", "contract C { modifier M { _; } }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0];
- BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement");
- BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1");
-}
-
-BOOST_AUTO_TEST_CASE(non_utf8)
-{
- CompilerStack c;
- c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }");
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1];
- BOOST_CHECK_EQUAL(literal["name"], "Literal");
- BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff");
- BOOST_CHECK_EQUAL(literal["attributes"]["token"], "string");
- BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue);
- BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos);
-}
-
-BOOST_AUTO_TEST_CASE(function_type)
-{
- CompilerStack c;
- c.addSource("a",
- "contract C { function f(function() external payable returns (uint) x) "
- "returns (function() external constant returns (uint)) {} }"
- );
- c.setEVMVersion(dev::test::Options::get().evmVersion());
- c.parseAndAnalyze();
- map<string, unsigned> sourceIndices;
- sourceIndices["a"] = 1;
- Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value fun = astJson["children"][0]["children"][0];
- BOOST_CHECK_EQUAL(fun["name"], "FunctionDefinition");
- Json::Value argument = fun["children"][0]["children"][0];
- BOOST_CHECK_EQUAL(argument["name"], "VariableDeclaration");
- BOOST_CHECK_EQUAL(argument["attributes"]["name"], "x");
- BOOST_CHECK_EQUAL(argument["attributes"]["type"], "function () payable external returns (uint256)");
- Json::Value funType = argument["children"][0];
- BOOST_CHECK_EQUAL(funType["attributes"]["constant"], false);
- BOOST_CHECK_EQUAL(funType["attributes"]["payable"], true);
- BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
- Json::Value retval = fun["children"][1]["children"][0];
- BOOST_CHECK_EQUAL(retval["name"], "VariableDeclaration");
- BOOST_CHECK_EQUAL(retval["attributes"]["name"], "");
- BOOST_CHECK_EQUAL(retval["attributes"]["type"], "function () view external returns (uint256)");
- funType = retval["children"][0];
- BOOST_CHECK_EQUAL(funType["attributes"]["constant"], true);
- BOOST_CHECK_EQUAL(funType["attributes"]["payable"], false);
- BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
+ Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
+ Json::Value varDecl = astJson["nodes"][0]["nodes"][1]["body"]["statements"][0]["initialValue"];
+ BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeIdentifier"], "t_array$_t_uint256_$dyn_storage");
+ BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeString"], "uint256[] storage ref");
}
BOOST_AUTO_TEST_CASE(documentation)
@@ -266,30 +121,17 @@ BOOST_AUTO_TEST_CASE(documentation)
sourceIndices["a"] = 0;
sourceIndices["b"] = 1;
sourceIndices["c"] = 2;
- Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
- Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"];
- BOOST_CHECK_EQUAL(documentationA, "This contract is empty");
- Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b"));
- Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"];
- BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment.");
- Json::Value astJsonC = ASTJsonConverter(true, sourceIndices).toJson(c.ast("c"));
- Json::Value documentationC0 = astJsonC["children"][0]["children"][0]["attributes"]["documentation"];
- Json::Value documentationC1 = astJsonC["children"][0]["children"][1]["attributes"]["documentation"];
- Json::Value documentationC2 = astJsonC["children"][0]["children"][2]["attributes"]["documentation"];
- BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt.");
- BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod.");
- BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn.");
//same tests for non-legacy mode
- astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
- documentationA = astJsonA["nodes"][0]["documentation"];
+ Json::Value astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a"));
+ Json::Value documentationA = astJsonA["nodes"][0]["documentation"];
BOOST_CHECK_EQUAL(documentationA, "This contract is empty");
- astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b"));
- documentationB = astJsonB["nodes"][0]["documentation"];
+ Json::Value astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b"));
+ Json::Value documentationB = astJsonB["nodes"][0]["documentation"];
BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment.");
- astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c"));
- documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"];
- documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"];
- documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"];
+ Json::Value astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c"));
+ Json::Value documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"];
+ Json::Value documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"];
+ Json::Value documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"];
BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt.");
BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod.");
BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn.");
diff --git a/test/libsolidity/ASTLegacyJSON.cpp b/test/libsolidity/ASTLegacyJSON.cpp
new file mode 100644
index 00000000..13148682
--- /dev/null
+++ b/test/libsolidity/ASTLegacyJSON.cpp
@@ -0,0 +1,324 @@
+/*
+ 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/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2016
+ * Tests for the json ast output.
+ */
+
+#include <test/Options.h>
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/CompilerStack.h>
+#include <libsolidity/ast/ASTJsonConverter.h>
+
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(SolidityASTLegacyJSON)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C {}");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit");
+}
+
+BOOST_AUTO_TEST_CASE(source_location)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { function f() { var x = 2; x++; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit");
+ BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition");
+ BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition");
+ BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["src"], "13:32:1");
+
+}
+
+BOOST_AUTO_TEST_CASE(inheritance_specifier)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C1 {} contract C2 is C1 {}");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2");
+ BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier");
+ BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1");
+ BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["name"], "UserDefinedTypeName");
+ BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["attributes"]["name"], "C1");
+}
+
+BOOST_AUTO_TEST_CASE(using_for_directive)
+{
+ CompilerStack c;
+ c.addSource("a", "library L {} contract C { using L for uint; }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value usingFor = astJson["children"][1]["children"][0];
+ BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective");
+ BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1");
+ BOOST_CHECK_EQUAL(usingFor["children"][0]["name"], "UserDefinedTypeName");
+ BOOST_CHECK_EQUAL(usingFor["children"][0]["attributes"]["name"], "L");
+ BOOST_CHECK_EQUAL(usingFor["children"][1]["name"], "ElementaryTypeName");
+ BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint");
+}
+
+BOOST_AUTO_TEST_CASE(enum_value)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { enum E { A, B } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value enumDefinition = astJson["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue");
+ BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A");
+ BOOST_CHECK_EQUAL(enumDefinition["children"][0]["src"], "22:1:1");
+ BOOST_CHECK_EQUAL(enumDefinition["children"][1]["name"], "EnumValue");
+ BOOST_CHECK_EQUAL(enumDefinition["children"][1]["attributes"]["name"], "B");
+ BOOST_CHECK_EQUAL(enumDefinition["children"][1]["src"], "25:1:1");
+}
+
+BOOST_AUTO_TEST_CASE(modifier_definition)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value modifier = astJson["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition");
+ BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M");
+ BOOST_CHECK_EQUAL(modifier["src"], "13:25:1");
+}
+
+BOOST_AUTO_TEST_CASE(modifier_invocation)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value modifier = astJson["children"][0]["children"][1]["children"][2];
+ BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation");
+ BOOST_CHECK_EQUAL(modifier["src"], "52:4:1");
+ BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["type"], "modifier (uint256)");
+ BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["value"], "M");
+ BOOST_CHECK_EQUAL(modifier["children"][1]["attributes"]["value"], "1");
+}
+
+BOOST_AUTO_TEST_CASE(event_definition)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { event E(); }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value event = astJson["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(event["name"], "EventDefinition");
+ BOOST_CHECK_EQUAL(event["attributes"]["name"], "E");
+ BOOST_CHECK_EQUAL(event["src"], "13:10:1");
+}
+
+BOOST_AUTO_TEST_CASE(array_type_name)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { uint[] i; }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value arrayDecl = astJson["children"][0]["children"][0]["attributes"];
+ BOOST_CHECK_EQUAL(arrayDecl["storageLocation"], "default");
+ BOOST_CHECK_EQUAL(arrayDecl["type"], "uint256[]");
+ Json::Value array = astJson["children"][0]["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName");
+ BOOST_CHECK_EQUAL(array["src"], "13:6:1");
+}
+
+BOOST_AUTO_TEST_CASE(short_type_name)
+{
+ CompilerStack c;
+ c.addSource("a", "contract c { function f() { uint[] memory x; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value arrayDecl = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(arrayDecl["attributes"]["storageLocation"], "memory");
+ BOOST_CHECK_EQUAL(arrayDecl["attributes"]["type"], "uint256[]");
+}
+
+BOOST_AUTO_TEST_CASE(short_type_name_ref)
+{
+ CompilerStack c;
+ c.addSource("a", "contract c { function f() { uint[][] memory rows; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value arrayDecl = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(arrayDecl["attributes"]["storageLocation"], "memory");
+ BOOST_CHECK_EQUAL(arrayDecl["attributes"]["type"], "uint256[][]");
+}
+
+BOOST_AUTO_TEST_CASE(placeholder_statement)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { modifier M { _; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0];
+ BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement");
+ BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1");
+}
+
+BOOST_AUTO_TEST_CASE(non_utf8)
+{
+ CompilerStack c;
+ c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }");
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value varDecl = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(varDecl["attributes"]["type"], "string");
+ BOOST_CHECK_EQUAL(varDecl["attributes"]["typeName"], Json::nullValue);
+ Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1];
+ BOOST_CHECK_EQUAL(literal["name"], "Literal");
+ BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff");
+ BOOST_CHECK_EQUAL(literal["attributes"]["token"], "string");
+ BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue);
+ BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos);
+}
+
+BOOST_AUTO_TEST_CASE(function_type)
+{
+ CompilerStack c;
+ c.addSource("a",
+ "contract C { function f(function() external payable returns (uint) x) "
+ "returns (function() external constant returns (uint)) {} }"
+ );
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 1;
+ Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value fun = astJson["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(fun["name"], "FunctionDefinition");
+ Json::Value argument = fun["children"][0]["children"][0];
+ BOOST_CHECK_EQUAL(argument["name"], "VariableDeclaration");
+ BOOST_CHECK_EQUAL(argument["attributes"]["name"], "x");
+ BOOST_CHECK_EQUAL(argument["attributes"]["type"], "function () payable external returns (uint256)");
+ Json::Value funType = argument["children"][0];
+ BOOST_CHECK_EQUAL(funType["attributes"]["constant"], false);
+ BOOST_CHECK_EQUAL(funType["attributes"]["payable"], true);
+ BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
+ Json::Value retval = fun["children"][1]["children"][0];
+ BOOST_CHECK_EQUAL(retval["name"], "VariableDeclaration");
+ BOOST_CHECK_EQUAL(retval["attributes"]["name"], "");
+ BOOST_CHECK_EQUAL(retval["attributes"]["type"], "function () view external returns (uint256)");
+ funType = retval["children"][0];
+ BOOST_CHECK_EQUAL(funType["attributes"]["constant"], true);
+ BOOST_CHECK_EQUAL(funType["attributes"]["payable"], false);
+ BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
+}
+
+BOOST_AUTO_TEST_CASE(documentation)
+{
+ CompilerStack c;
+ c.addSource("a", "/**This contract is empty*/ contract C {}");
+ c.addSource("b",
+ "/**This contract is empty"
+ " and has a line-breaking comment.*/"
+ "contract C {}"
+ );
+ c.addSource("c",
+ "contract C {"
+ " /** Some comment on Evt.*/ event Evt();"
+ " /** Some comment on mod.*/ modifier mod() { _; }"
+ " /** Some comment on fn.*/ function fn() public {}"
+ "}"
+ );
+ c.setEVMVersion(dev::test::Options::get().evmVersion());
+ c.parseAndAnalyze();
+ map<string, unsigned> sourceIndices;
+ sourceIndices["a"] = 0;
+ sourceIndices["b"] = 1;
+ sourceIndices["c"] = 2;
+ Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a"));
+ Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"];
+ BOOST_CHECK_EQUAL(documentationA, "This contract is empty");
+ Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b"));
+ Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"];
+ BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment.");
+ Json::Value astJsonC = ASTJsonConverter(true, sourceIndices).toJson(c.ast("c"));
+ Json::Value documentationC0 = astJsonC["children"][0]["children"][0]["attributes"]["documentation"];
+ Json::Value documentationC1 = astJsonC["children"][0]["children"][1]["attributes"]["documentation"];
+ Json::Value documentationC2 = astJsonC["children"][0]["children"][2]["attributes"]["documentation"];
+ BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt.");
+ BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod.");
+ BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn.");
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp
index 7c335a48..72b86767 100644
--- a/test/libsolidity/AnalysisFramework.cpp
+++ b/test/libsolidity/AnalysisFramework.cpp
@@ -20,7 +20,7 @@
#include <test/libsolidity/AnalysisFramework.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/SourceReferenceFormatter.h>
@@ -56,12 +56,23 @@ AnalysisFramework::parseAnalyseAndReturnError(
m_compiler.analyze();
+ ErrorList errors = filterErrors(m_compiler.errors(), _reportWarnings);
+ if (errors.size() > 1 && !_allowMultipleErrors)
+ BOOST_FAIL("Multiple errors found: " + formatErrors());
+
+ return make_pair(&m_compiler.ast(""), std::move(errors));
+}
+
+ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarnings) const
+{
ErrorList errors;
- for (auto const& currentError: m_compiler.errors())
+ for (auto const& currentError: _errorList)
{
solAssert(currentError->comment(), "");
if (currentError->type() == Error::Type::Warning)
{
+ if (!_includeWarnings)
+ continue;
bool ignoreWarning = false;
for (auto const& filter: m_warningsToFilter)
if (currentError->comment()->find(filter) == 0)
@@ -73,17 +84,10 @@ AnalysisFramework::parseAnalyseAndReturnError(
continue;
}
- if (_reportWarnings || (currentError->type() != Error::Type::Warning))
- {
- if (!_allowMultipleErrors && !errors.empty())
- {
- BOOST_FAIL("Multiple errors found: " + formatErrors());
- }
- errors.emplace_back(std::move(currentError));
- }
+ errors.emplace_back(currentError);
}
- return make_pair(&m_compiler.ast(""), errors);
+ return errors;
}
SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source)
@@ -110,7 +114,7 @@ ErrorList AnalysisFramework::expectError(std::string const& _source, bool _warni
return sourceAndErrors.second;
}
-string AnalysisFramework::formatErrors()
+string AnalysisFramework::formatErrors() const
{
string message;
for (auto const& error: m_compiler.errors())
@@ -118,7 +122,7 @@ string AnalysisFramework::formatErrors()
return message;
}
-string AnalysisFramework::formatError(Error const& _error)
+string AnalysisFramework::formatError(Error const& _error) const
{
return SourceReferenceFormatter::formatExceptionInformation(
_error,
diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h
index 6ecf4a5a..05490a42 100644
--- a/test/libsolidity/AnalysisFramework.h
+++ b/test/libsolidity/AnalysisFramework.h
@@ -57,8 +57,8 @@ protected:
bool success(std::string const& _source);
ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false);
- std::string formatErrors();
- std::string formatError(Error const& _error);
+ std::string formatErrors() const;
+ std::string formatError(Error const& _error) const;
static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name);
static FunctionTypePointer retrieveFunctionBySignature(
@@ -66,6 +66,9 @@ protected:
std::string const& _signature
);
+ // filter out the warnings in m_warningsToFilter or all warnings if _includeWarnings is false
+ ErrorList filterErrors(ErrorList const& _errorList, bool _includeWarnings) const;
+
std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"};
dev::solidity::CompilerStack m_compiler;
};
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index aff610a4..77ca363a 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -20,7 +20,7 @@
* Unit tests for Assembly Items from evmasm/Assembly.h
*/
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Assembly.h>
@@ -157,14 +157,23 @@ BOOST_AUTO_TEST_CASE(location_test)
}
}
)";
- shared_ptr<string const> n = make_shared<string>("");
AssemblyItems items = compileContract(sourceCode);
vector<SourceLocation> locations =
- vector<SourceLocation>(24, SourceLocation(2, 75, n)) +
- vector<SourceLocation>(32, SourceLocation(20, 72, n)) +
- vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
- vector<SourceLocation>(2, SourceLocation(58, 67, n)) +
- vector<SourceLocation>(2, SourceLocation(20, 72, n));
+ vector<SourceLocation>(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--"))) +
+ vector<SourceLocation>(1, SourceLocation(30, 31, make_shared<string>("--CODEGEN--"))) +
+ vector<SourceLocation>(1, SourceLocation(27, 28, make_shared<string>("--CODEGEN--"))) +
+ vector<SourceLocation>(1, SourceLocation(20, 32, make_shared<string>("--CODEGEN--"))) +
+ vector<SourceLocation>(1, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) +
+ vector<SourceLocation>(24, SourceLocation(20, 72, make_shared<string>(""))) +
+ vector<SourceLocation>(1, SourceLocation(42, 51, make_shared<string>(""))) +
+ 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/FormattedScope.h b/test/libsolidity/FormattedScope.h
new file mode 100644
index 00000000..923404f0
--- /dev/null
+++ b/test/libsolidity/FormattedScope.h
@@ -0,0 +1,68 @@
+/*
+ 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 <boost/noncopyable.hpp>
+
+#include <ostream>
+#include <vector>
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace formatting
+{
+
+static constexpr char const* RESET = "\033[0m";
+static constexpr char const* RED = "\033[1;31m";
+static constexpr char const* GREEN = "\033[1;32m";
+static constexpr char const* YELLOW = "\033[1;33m";
+static constexpr char const* CYAN = "\033[1;36m";
+static constexpr char const* BOLD = "\033[1m";
+static constexpr char const* RED_BACKGROUND = "\033[48;5;160m";
+static constexpr char const* ORANGE_BACKGROUND = "\033[48;5;166m";
+static constexpr char const* INVERSE = "\033[7m";
+
+}
+
+class FormattedScope: boost::noncopyable
+{
+public:
+ /// @arg _formatting List of formatting strings (e.g. colors) defined in the formatting namespace.
+ FormattedScope(std::ostream& _stream, bool const _enabled, std::vector<char const*> const& _formatting):
+ m_stream(_stream), m_enabled(_enabled)
+ {
+ if (m_enabled)
+ for (auto const& format: _formatting)
+ m_stream << format;
+ }
+ ~FormattedScope() { if (m_enabled) m_stream << formatting::RESET; }
+ template<typename T>
+ std::ostream& operator<<(T&& _t) { return m_stream << std::forward<T>(_t); }
+private:
+ std::ostream& m_stream;
+ bool m_enabled;
+};
+
+}
+}
+}
diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp
index fd2017f9..0d66456c 100644
--- a/test/libsolidity/GasMeter.cpp
+++ b/test/libsolidity/GasMeter.cpp
@@ -294,6 +294,19 @@ BOOST_AUTO_TEST_CASE(extcodesize_gas)
testRunTimeGas("f()", vector<bytes>{encodeArgs()});
}
+BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback)
+{
+ // A bug in the estimator caused the costs for a specific function
+ // to always include the costs for the fallback.
+ char const* sourceCode = R"(
+ contract A {
+ uint public x;
+ function() { x = 2; }
+ }
+ )";
+ testCreationTimeGas(sourceCode);
+ testRunTimeGas("x()", vector<bytes>{encodeArgs()});
+}
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp
index bc81b3b1..1b5dd4a5 100644
--- a/test/libsolidity/Imports.cpp
+++ b/test/libsolidity/Imports.cpp
@@ -21,7 +21,7 @@
*/
#include <test/libsolidity/ErrorCheck.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/CompilerStack.h>
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index a4dcc4d5..0ced1792 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -20,7 +20,7 @@
* Unit tests for inline assembly.
*/
-#include "../TestHelper.h"
+#include <test/Options.h>
#include <libsolidity/interface/AssemblyStack.h>
#include <libsolidity/parsing/Scanner.h>
@@ -783,6 +783,8 @@ BOOST_AUTO_TEST_CASE(shift)
BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
{
+ if (dev::test::Options::get().evmVersion().hasBitwiseShifting())
+ return;
CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available for Constantinople-compatible VMs.");
CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available for Constantinople-compatible VMs.");
CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available for Constantinople-compatible VMs.");
diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp
index 285c5604..2b3df3a7 100644
--- a/test/libsolidity/JSONCompiler.cpp
+++ b/test/libsolidity/JSONCompiler.cpp
@@ -25,8 +25,8 @@
#include <libsolidity/interface/Version.h>
#include <libsolc/libsolc.h>
-#include "../Metadata.h"
-#include "../TestHelper.h"
+#include <test/Metadata.h>
+#include <test/Options.h>
using namespace std;
@@ -111,18 +111,18 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["bytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
- "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00"
+ "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00"
);
BOOST_CHECK(contract["runtimeBytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
- "6060604052600080fd00"
+ "6080604052600080fd00"
);
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());
BOOST_CHECK_EQUAL(
dev::jsonCompactPrint(contract["gasEstimates"]),
- "{\"creation\":[61,10600],\"external\":{},\"internal\":{}}"
+ "{\"creation\":[66,10600],\"external\":{},\"internal\":{}}"
);
BOOST_CHECK(contract["metadata"].isString());
BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString()));
@@ -153,18 +153,18 @@ BOOST_AUTO_TEST_CASE(single_compilation)
BOOST_CHECK(contract["bytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
- "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00"
+ "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00"
);
BOOST_CHECK(contract["runtimeBytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
- "6060604052600080fd00"
+ "6080604052600080fd00"
);
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());
BOOST_CHECK_EQUAL(
dev::jsonCompactPrint(contract["gasEstimates"]),
- "{\"creation\":[61,10600],\"external\":{},\"internal\":{}}"
+ "{\"creation\":[66,10600],\"external\":{},\"internal\":{}}"
);
BOOST_CHECK(contract["metadata"].isString());
BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString()));
diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp
index f1edeeb7..808bd1e1 100644
--- a/test/libsolidity/Metadata.cpp
+++ b/test/libsolidity/Metadata.cpp
@@ -19,8 +19,8 @@
* Unit tests for the metadata output.
*/
-#include "../Metadata.h"
-#include "../TestHelper.h"
+#include <test/Metadata.h>
+#include <test/Options.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libdevcore/SwarmHash.h>
#include <libdevcore/JSON.h>
diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp
index 12b5f439..beb933a4 100644
--- a/test/libsolidity/SMTChecker.cpp
+++ b/test/libsolidity/SMTChecker.cpp
@@ -329,6 +329,144 @@ BOOST_AUTO_TEST_CASE(ways_to_merge_variables)
CHECK_WARNING(text, "Assertion violation happens here");
}
+BOOST_AUTO_TEST_CASE(bool_simple)
+{
+ string text = R"(
+ contract C {
+ function f(bool x) public pure {
+ assert(x);
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Assertion violation happens here");
+ text = R"(
+ contract C {
+ function f(bool x, bool y) public pure {
+ assert(x == y);
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Assertion violation happens here");
+ text = R"(
+ contract C {
+ function f(bool x, bool y) public pure {
+ bool z = x || y;
+ assert(!(x && y) || z);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x) public pure {
+ if(x) {
+ assert(x);
+ } else {
+ assert(!x);
+ }
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x) public pure {
+ bool y = x;
+ assert(x == y);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x) public pure {
+ require(x);
+ bool y;
+ y = false;
+ assert(x || y);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x) public pure {
+ bool y;
+ assert(x <= y);
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Assertion violation happens here");
+ text = R"(
+ contract C {
+ function f(bool x) public pure {
+ bool y;
+ assert(x >= y);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x) public pure {
+ require(x);
+ bool y;
+ assert(x > y);
+ assert(y < x);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(bool_int_mixed)
+{
+ string text = R"(
+ contract C {
+ function f(bool x) public pure {
+ uint a;
+ if(x)
+ a = 1;
+ assert(!x || a > 0);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x, uint a) public pure {
+ require(!x || a > 0);
+ uint b = a;
+ assert(!x || b > 0);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+ text = R"(
+ contract C {
+ function f(bool x, bool y) public pure {
+ uint a;
+ if (x) {
+ if (y) {
+ a = 0;
+ } else {
+ a = 1;
+ }
+ } else {
+ if (y) {
+ a = 1;
+ } else {
+ a = 0;
+ }
+ }
+ bool xor_x_y = (x && !y) || (!x && y);
+ assert(!xor_x_y || a > 0);
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
BOOST_AUTO_TEST_CASE(while_loop_simple)
{
// Check that variables are cleared
diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp
index 08ef5277..07f8fba6 100644
--- a/test/libsolidity/SemVerMatcher.cpp
+++ b/test/libsolidity/SemVerMatcher.cpp
@@ -25,7 +25,7 @@
#include <tuple>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/analysis/SemVerHandler.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index 0d471b32..107abc26 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -20,7 +20,7 @@
* Unit tests for the solidity compiler JSON Interface output.
*/
-#include "../TestHelper.h"
+#include <test/Options.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libdevcore/Exceptions.h>
diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp
new file mode 100644
index 00000000..90540f3e
--- /dev/null
+++ b/test/libsolidity/SolidityCompiler.cpp
@@ -0,0 +1,60 @@
+/*
+ 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/>.
+ */
+/**
+ * Unit tests for the compiler itself.
+ */
+
+#include <test/libsolidity/AnalysisFramework.h>
+
+#include <test/Options.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+BOOST_FIXTURE_TEST_SUITE(Compiler, AnalysisFramework)
+
+BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions)
+{
+ char const* sourceCode = R"(
+ contract C {
+ uint x;
+ function C() { f(); }
+ function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; }
+ }
+ )";
+ BOOST_REQUIRE(success(sourceCode));
+ m_compiler.setOptimiserSettings(dev::test::Options::get().optimize);
+ BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed");
+ bytes const& creationBytecode = m_compiler.object("C").bytecode;
+ bytes const& runtimeBytecode = m_compiler.runtimeObject("C").bytecode;
+ BOOST_CHECK(creationBytecode.size() >= 130);
+ BOOST_CHECK(creationBytecode.size() <= 160);
+ BOOST_CHECK(runtimeBytecode.size() >= 50);
+ BOOST_CHECK(runtimeBytecode.size() <= 70);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 33cd1419..789b89d1 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -23,7 +23,7 @@
#include <test/libsolidity/SolidityExecutionFramework.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/interface/Exceptions.h>
#include <libsolidity/interface/EVMVersion.h>
@@ -102,6 +102,29 @@ BOOST_AUTO_TEST_CASE(exp_operator_const_signed)
ABI_CHECK(callContractFunction("f()", bytes()), toBigEndian(u256(-8)));
}
+BOOST_AUTO_TEST_CASE(exp_zero)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f(uint a) returns(uint d) { return a ** 0; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ testContractAgainstCppOnRange("f(uint256)", [](u256 const&) -> u256 { return u256(1); }, 0, 16);
+}
+
+BOOST_AUTO_TEST_CASE(exp_zero_literal)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns(uint d) { return 0 ** 0; }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()", bytes()), toBigEndian(u256(1)));
+}
+
+
BOOST_AUTO_TEST_CASE(conditional_expression_true_literal)
{
char const* sourceCode = R"(
@@ -1788,6 +1811,35 @@ BOOST_AUTO_TEST_CASE(transfer_ether)
ABI_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10), encodeArgs());
}
+BOOST_AUTO_TEST_CASE(uncalled_blockhash)
+{
+ char const* code = R"(
+ contract C {
+ function f() public view returns (bytes32)
+ {
+ var x = block.blockhash;
+ return x(block.number - 1);
+ }
+ }
+ )";
+ compileAndRun(code, 0, "C");
+ bytes result = callContractFunction("f()");
+ BOOST_REQUIRE_EQUAL(result.size(), 32);
+ BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0);
+}
+
+BOOST_AUTO_TEST_CASE(blockhash_shadow_resolution)
+{
+ char const* code = R"(
+ contract C {
+ function blockhash(uint256 blockNumber) public returns(bytes32) { bytes32 x; return x; }
+ function f() public returns(bytes32) { return blockhash(3); }
+ }
+ )";
+ compileAndRun(code, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
+}
+
BOOST_AUTO_TEST_CASE(log0)
{
char const* sourceCode = R"(
@@ -2048,6 +2100,31 @@ BOOST_AUTO_TEST_CASE(packed_keccak256)
testContractAgainstCpp("a(bytes32)", f, u256(-1));
}
+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) {
+ uint120[] memory y = new uint120[](3);
+ x[0] = y[0] = uint120(-2);
+ x[1] = y[1] = uint120(-3);
+ x[2] = y[2] = uint120(-4);
+ hash1 = keccak256(x);
+ hash2 = keccak256(y);
+ hash3 = keccak256(this.f);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ // Strangely, arrays are encoded with intra-element padding.
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(
+ dev::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))),
+ dev::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))),
+ dev::keccak256(fromHex(m_contractAddress.hex() + "26121ff0"))
+ ));
+}
+
BOOST_AUTO_TEST_CASE(packed_sha256)
{
char const* sourceCode = R"(
@@ -2879,6 +2956,58 @@ BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars)
ABI_CHECK(callContractFunction("a()"), encodeArgs(0));
}
+BOOST_AUTO_TEST_CASE(function_modifier_library)
+{
+ char const* sourceCode = R"(
+ library L {
+ struct S { uint v; }
+ modifier mod(S storage s) { s.v++; _; }
+ function libFun(S storage s) mod(s) internal { s.v += 0x100; }
+ }
+
+ contract Test {
+ using L for *;
+ L.S s;
+
+ function f() public returns (uint) {
+ s.libFun();
+ L.libFun(s);
+ return s.v;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202));
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_library_inheritance)
+{
+ // Tests that virtual lookup for modifiers in libraries does not consider
+ // the current inheritance hierarchy.
+
+ char const* sourceCode = R"(
+ library L {
+ struct S { uint v; }
+ modifier mod(S storage s) { s.v++; _; }
+ function libFun(S storage s) mod(s) internal { s.v += 0x100; }
+ }
+
+ contract Test {
+ using L for *;
+ L.S s;
+ modifier mod(L.S storage) { revert(); _; }
+
+ function f() public returns (uint) {
+ s.libFun();
+ L.libFun(s);
+ return s.v;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202));
+}
+
BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack)
{
char const* sourceCode = R"(
@@ -3451,6 +3580,45 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter)
ABI_CHECK(callContractFunction("f(uint256)", 9), encodeArgs(9));
}
+BOOST_AUTO_TEST_CASE(sha256_empty)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (bytes32) {
+ return sha256();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"));
+}
+
+BOOST_AUTO_TEST_CASE(ripemd160_empty)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (bytes20) {
+ return ripemd160();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), fromHex("0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000"));
+}
+
+BOOST_AUTO_TEST_CASE(keccak256_empty)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (bytes32) {
+ return keccak256();
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"));
+}
+
BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments)
{
char const* sourceCode = R"(
@@ -4778,6 +4946,48 @@ BOOST_AUTO_TEST_CASE(array_push)
ABI_CHECK(callContractFunction("test()"), encodeArgs(5, 4, 3, 3));
}
+BOOST_AUTO_TEST_CASE(array_push_struct)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; }
+ S[] data;
+ function test() returns (uint16, uint16, uint16, uint16) {
+ S memory s;
+ s.a = 2;
+ s.b = 3;
+ s.c[2] = 4;
+ s.d = new uint16[](4);
+ s.d[2] = 5;
+ data.push(s);
+ return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 3, 4, 5));
+}
+
+BOOST_AUTO_TEST_CASE(array_push_packed_array)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint80[] x;
+ function test() returns (uint80, uint80, uint80, uint80) {
+ x.push(1);
+ x.push(2);
+ x.push(3);
+ x.push(4);
+ x.push(5);
+ x.length = 4;
+ return (x[0], x[1], x[2], x[3]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3, 4));
+}
+
BOOST_AUTO_TEST_CASE(byte_array_push)
{
char const* sourceCode = R"(
@@ -4798,6 +5008,29 @@ BOOST_AUTO_TEST_CASE(byte_array_push)
ABI_CHECK(callContractFunction("test()"), encodeArgs(false));
}
+BOOST_AUTO_TEST_CASE(byte_array_push_transition)
+{
+ // Tests transition between short and long encoding
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function test() returns (uint) {
+ for (uint i = 1; i < 40; i++)
+ {
+ data.push(byte(i));
+ if (data.length != i) return 0x1000 + i;
+ if (data[data.length - 1] != byte(i)) return i;
+ }
+ for (i = 1; i < 40; i++)
+ if (data[i - 1] != byte(i)) return 0x1000000 + i;
+ return 0;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("test()"), encodeArgs(0));
+}
+
BOOST_AUTO_TEST_CASE(external_array_args)
{
char const* sourceCode = R"(
@@ -5020,7 +5253,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base)
}
uint public m_i;
}
- contract Derived is Base(2) {
+ contract Derived is Base {
function Derived(uint i) Base(i)
{}
}
@@ -5040,10 +5273,10 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base)
}
uint public m_i;
}
- contract Base1 is Base(3) {
+ contract Base1 is Base {
function Base1(uint k) Base(k*k) {}
}
- contract Derived is Base(3), Base1(2) {
+ contract Derived is Base, Base1 {
function Derived(uint i) Base(i) Base1(i)
{}
}
@@ -5064,7 +5297,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap)
uint public m_i;
}
contract Base1 is Base(3) {}
- contract Derived is Base(2), Base1 {
+ contract Derived is Base, Base1 {
function Derived(uint i) Base(i) {}
}
contract Final is Derived(4) {
@@ -6955,6 +7188,21 @@ BOOST_AUTO_TEST_CASE(library_call)
ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(33) * 9));
}
+BOOST_AUTO_TEST_CASE(library_function_external)
+{
+ char const* sourceCode = R"(
+ library Lib { function m(bytes b) external pure returns (byte) { return b[2]; } }
+ contract Test {
+ function f(bytes b) public pure returns (byte) {
+ return Lib.m(b);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), "abcde"), encodeArgs("c"));
+}
+
BOOST_AUTO_TEST_CASE(library_stray_values)
{
char const* sourceCode = R"(
@@ -7566,7 +7814,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size)
ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256));
}
-
BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes)
{
// Computes binomial coefficients the chinese way
@@ -7589,6 +7836,41 @@ BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes)
ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70)));
}
+BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ uint[][] memory x = new uint[][](42);
+ assert(x[0].length == 0);
+ x[0] = new uint[](1);
+ x[0][0] = 1;
+ assert(x[4].length == 0);
+ x[4] = new uint[](1);
+ x[4][0] = 2;
+ assert(x[10].length == 0);
+ x[10] = new uint[](1);
+ x[10][0] = 44;
+ uint[][] memory y = new uint[][](24);
+ assert(y[0].length == 0);
+ y[0] = new uint[](1);
+ y[0][0] = 1;
+ assert(y[4].length == 0);
+ y[4] = new uint[](1);
+ y[4][0] = 2;
+ assert(y[10].length == 0);
+ y[10] = new uint[](1);
+ y[10][0] = 88;
+ if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88))
+ return 7;
+ return 0;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7)));
+}
+
BOOST_AUTO_TEST_CASE(memory_overwrite)
{
char const* sourceCode = R"(
@@ -7627,12 +7909,12 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero)
{
char const* sourceCode = R"(
contract C {
- function f() pure returns (uint) {
- addmod(1, 2, 0);
+ function f(uint d) pure returns (uint) {
+ addmod(1, 2, d);
return 2;
}
- function g() pure returns (uint) {
- mulmod(1, 2, 0);
+ function g(uint d) pure returns (uint) {
+ mulmod(1, 2, d);
return 2;
}
function h() pure returns (uint) {
@@ -7645,8 +7927,8 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero)
}
)";
compileAndRun(sourceCode);
- ABI_CHECK(callContractFunction("f()"), encodeArgs());
- ABI_CHECK(callContractFunction("g()"), encodeArgs());
+ ABI_CHECK(callContractFunction("f(uint)", 0), encodeArgs());
+ ABI_CHECK(callContractFunction("g(uint)", 0), encodeArgs());
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
}
@@ -8548,6 +8830,24 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types)
ABI_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)), encodeArgs(0));
}
+BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() pure returns (bytes32 r) {
+ bytes4 x = 0xffffffff;
+ bytes2 y = bytes2(x);
+ assembly { r := y }
+ // At this point, r and y both store four bytes, but
+ // y is properly cleaned before the equality check
+ require(y == bytes2(0xffff));
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs("\xff\xff\xff\xff"));
+}
+
BOOST_AUTO_TEST_CASE(skip_dynamic_types)
{
// The EVM cannot provide access to dynamically-sized return values, so we have to skip them.
@@ -8635,6 +8935,32 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length)
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7)));
}
+BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor)
+{
+ // Memory arrays are initialized using codecopy past the size of the code.
+ // This test checks that it also works in the constructor context.
+ char const* sourceCode = R"(
+ contract C {
+ bool public success;
+ function C() public {
+ // Make memory dirty.
+ assembly {
+ for { let i := 0 } lt(i, 64) { i := add(i, 1) } {
+ mstore(msize, not(0))
+ }
+ }
+ uint16[3] memory c;
+ require(c[0] == 0 && c[1] == 0 && c[2] == 0);
+ uint16[] memory x = new uint16[](3);
+ require(x[0] == 0 && x[1] == 0 && x[2] == 0);
+ success = true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("success()"), encodeArgs(u256(1)));
+}
+
BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier)
{
char const* sourceCode = R"(
@@ -8998,7 +9324,7 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail)
int mutex;
function t() returns (uint) {
if (mutex > 0)
- return 7;
+ { assembly { mstore(0, 7) return(0, 0x20) } }
mutex = 1;
// Avoid re-executing this function if we jump somewhere.
x();
@@ -9011,6 +9337,27 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail)
ABI_CHECK(callContractFunction("t()"), encodeArgs());
}
+BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array)
+{
+ char const* sourceCode = R"(
+ contract C {
+ int mutex;
+ function t() returns (uint) {
+ if (mutex > 0)
+ { assembly { mstore(0, 7) return(0, 0x20) } }
+ mutex = 1;
+ // Avoid re-executing this function if we jump somewhere.
+ function() internal returns (uint)[200] x;
+ x[0]();
+ return 2;
+ }
+ }
+ )";
+
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("t()"), encodeArgs());
+}
+
BOOST_AUTO_TEST_CASE(pass_function_types_internally)
{
char const* sourceCode = R"(
@@ -10152,6 +10499,214 @@ BOOST_AUTO_TEST_CASE(revert)
ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(42)));
}
+BOOST_AUTO_TEST_CASE(revert_with_cause)
+{
+ char const* sourceCode = R"(
+ contract D {
+ function f() public {
+ revert("test123");
+ }
+ function g() public {
+ revert("test1234567890123456789012345678901234567890");
+ }
+ }
+ contract C {
+ D d = new D();
+ function forward(address target, bytes data) internal returns (bool success, bytes retval) {
+ uint retsize;
+ assembly {
+ success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0)
+ retsize := returndatasize()
+ }
+ retval = new bytes(retsize);
+ assembly {
+ returndatacopy(add(retval, 0x20), 0, returndatasize())
+ }
+ }
+ function f() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function g() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata();
+ 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());
+}
+
+BOOST_AUTO_TEST_CASE(require_with_message)
+{
+ char const* sourceCode = R"(
+ contract D {
+ bool flag = false;
+ string storageError = "abc";
+ function f(uint x) public {
+ require(x > 7, "failed");
+ }
+ function g() public {
+ // As a side-effect of internalFun, the flag will be set to true
+ // (even if the condition is true),
+ // but it will only throw in the next evaluation.
+ bool flagCopy = flag;
+ require(flagCopy == false, internalFun());
+ }
+ function internalFun() returns (string) {
+ flag = true;
+ return "only on second run";
+ }
+ function h() public {
+ require(false, storageError);
+ }
+ }
+ contract C {
+ D d = new D();
+ function forward(address target, bytes data) internal returns (bool success, bytes retval) {
+ uint retsize;
+ assembly {
+ success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0)
+ retsize := returndatasize()
+ }
+ retval = new bytes(retsize);
+ assembly {
+ returndatacopy(add(retval, 0x20), 0, returndatasize())
+ }
+ }
+ function f(uint x) public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function g() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function h() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata();
+ bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0};
+ ABI_CHECK(callContractFunction("f(uint256)", 8), haveReturndata ? encodeArgs(1, 0x40, 0) : bytes());
+ ABI_CHECK(callContractFunction("f(uint256)", 5), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 6, "failed") + bytes(28, 0) : bytes());
+ 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());
+}
+
+BOOST_AUTO_TEST_CASE(bubble_up_error_messages)
+{
+ char const* sourceCode = R"(
+ contract D {
+ function f() public {
+ revert("message");
+ }
+ function g() public {
+ this.f();
+ }
+ }
+ contract C {
+ D d = new D();
+ function forward(address target, bytes data) internal returns (bool success, bytes retval) {
+ uint retsize;
+ assembly {
+ success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0)
+ retsize := returndatasize()
+ }
+ retval = new bytes(retsize);
+ assembly {
+ returndatacopy(add(retval, 0x20), 0, returndatasize())
+ }
+ }
+ function f() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ function g() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata();
+ bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0};
+ ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes());
+ ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes());
+}
+
+BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer)
+{
+ char const* sourceCode = R"(
+ contract D {
+ function() public payable {
+ revert("message");
+ }
+ function f() public {
+ this.transfer(0);
+ }
+ }
+ contract C {
+ D d = new D();
+ function forward(address target, bytes data) internal returns (bool success, bytes retval) {
+ uint retsize;
+ assembly {
+ success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0)
+ retsize := returndatasize()
+ }
+ retval = new bytes(retsize);
+ assembly {
+ returndatacopy(add(retval, 0x20), 0, returndatasize())
+ }
+ }
+ function f() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata();
+ bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0};
+ ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes());
+}
+
+BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create)
+{
+ char const* sourceCode = R"(
+ contract E {
+ function E() {
+ revert("message");
+ }
+ }
+ contract D {
+ function f() public {
+ var x = new E();
+ }
+ }
+ contract C {
+ D d = new D();
+ function forward(address target, bytes data) internal returns (bool success, bytes retval) {
+ uint retsize;
+ assembly {
+ success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0)
+ retsize := returndatasize()
+ }
+ retval = new bytes(retsize);
+ assembly {
+ returndatacopy(add(retval, 0x20), 0, returndatasize())
+ }
+ }
+ function f() public returns (bool, bytes) {
+ return forward(address(d), msg.data);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata();
+ bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0};
+ ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes());
+}
+
BOOST_AUTO_TEST_CASE(negative_stack_height)
{
// This code was causing negative stack height during code generation
@@ -10785,6 +11340,329 @@ BOOST_AUTO_TEST_CASE(snark)
BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true));
}
+BOOST_AUTO_TEST_CASE(abi_encode)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f0() returns (bytes) {
+ return abi.encode();
+ }
+ function f1() returns (bytes) {
+ return abi.encode(1, 2);
+ }
+ function f2() returns (bytes) {
+ string memory x = "abc";
+ return abi.encode(1, x, 2);
+ }
+ function f3() returns (bytes r) {
+ // test that memory is properly allocated
+ string memory x = "abc";
+ r = abi.encode(1, x, 2);
+ bytes memory y = "def";
+ require(y[0] == "d");
+ y[0] = "e";
+ require(y[0] == "e");
+ }
+ function f4() returns (bytes) {
+ bytes4 x = "abcd";
+ return abi.encode(bytes2(x));
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0));
+ ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2));
+ ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc"));
+ ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc"));
+ ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x20, "ab"));
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_v2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ struct S { uint a; uint[] b; }
+ function f0() public pure returns (bytes) {
+ return abi.encode();
+ }
+ function f1() public pure returns (bytes) {
+ return abi.encode(1, 2);
+ }
+ function f2() public pure returns (bytes) {
+ string memory x = "abc";
+ return abi.encode(1, x, 2);
+ }
+ function f3() public pure returns (bytes r) {
+ // test that memory is properly allocated
+ string memory x = "abc";
+ r = abi.encode(1, x, 2);
+ bytes memory y = "def";
+ require(y[0] == "d");
+ y[0] = "e";
+ require(y[0] == "e");
+ }
+ S s;
+ function f4() public view returns (bytes r) {
+ string memory x = "abc";
+ s.a = 7;
+ s.b.push(2);
+ s.b.push(3);
+ r = abi.encode(1, x, s, 2);
+ bytes memory y = "def";
+ require(y[0] == "d");
+ y[0] = "e";
+ require(y[0] == "e");
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0));
+ ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2));
+ ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc"));
+ ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc"));
+ ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x160, 1, 0x80, 0xc0, 2, 3, "abc", 7, 0x40, 2, 2, 3));
+}
+
+
+BOOST_AUTO_TEST_CASE(abi_encodePacked)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f0() public pure returns (bytes) {
+ return abi.encodePacked();
+ }
+ function f1() public pure returns (bytes) {
+ return abi.encodePacked(uint8(1), uint8(2));
+ }
+ function f2() public pure returns (bytes) {
+ string memory x = "abc";
+ return abi.encodePacked(uint8(1), x, uint8(2));
+ }
+ function f3() public pure returns (bytes r) {
+ // test that memory is properly allocated
+ string memory x = "abc";
+ r = abi.encodePacked(uint8(1), x, uint8(2));
+ bytes memory y = "def";
+ require(y[0] == "d");
+ y[0] = "e";
+ require(y[0] == "e");
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0));
+ ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02"));
+ ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02"));
+ ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02"));
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_with_selector)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f0() public pure returns (bytes) {
+ return abi.encodeWithSelector(0x12345678);
+ }
+ function f1() public pure returns (bytes) {
+ return abi.encodeWithSelector(0x12345678, "abc");
+ }
+ function f2() public pure returns (bytes) {
+ bytes4 x = 0x12345678;
+ return abi.encodeWithSelector(x, "abc");
+ }
+ function f3() public pure returns (bytes) {
+ bytes4 x = 0x12345678;
+ return abi.encodeWithSelector(x, uint(-1));
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78"));
+ bytes expectation;
+ expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f1()"), expectation);
+ expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f2()"), expectation);
+ expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f3()"), expectation);
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2)
+{
+ char const* sourceCode = R"(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ function f0() public pure returns (bytes) {
+ return abi.encodeWithSelector(0x12345678);
+ }
+ function f1() public pure returns (bytes) {
+ return abi.encodeWithSelector(0x12345678, "abc");
+ }
+ function f2() public pure returns (bytes) {
+ bytes4 x = 0x12345678;
+ return abi.encodeWithSelector(x, "abc");
+ }
+ function f3() public pure returns (bytes) {
+ bytes4 x = 0x12345678;
+ return abi.encodeWithSelector(x, uint(-1));
+ }
+ struct S { uint a; string b; uint16 c; }
+ function f4() public pure returns (bytes) {
+ bytes4 x = 0x12345678;
+ S memory s;
+ s.a = 0x1234567;
+ s.b = "Lorem ipsum dolor sit ethereum........";
+ s.c = 0x1234;
+ return abi.encodeWithSelector(x, uint(-1), s, uint(3));
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78"));
+ bytes expectation;
+ expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f1()"), expectation);
+ expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f2()"), expectation);
+ expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f3()"), expectation);
+ expectation =
+ encodeArgs(0x20, 4 + 0x120) +
+ bytes{0x12, 0x34, 0x56, 0x78} +
+ encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") +
+ bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f4()"), expectation);
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_with_signature)
+{
+ char const* sourceCode = R"T(
+ contract C {
+ function f0() public pure returns (bytes) {
+ return abi.encodeWithSignature("f(uint256)");
+ }
+ function f1() public pure returns (bytes) {
+ string memory x = "f(uint256)";
+ return abi.encodeWithSignature(x, "abc");
+ }
+ string xstor;
+ function f1s() public returns (bytes) {
+ xstor = "f(uint256)";
+ return abi.encodeWithSignature(xstor, "abc");
+ }
+ function f2() public pure returns (bytes r, uint[] ar) {
+ string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
+ uint[] memory y = new uint[](4);
+ y[0] = uint(-1);
+ y[1] = uint(-2);
+ y[2] = uint(-3);
+ y[3] = uint(-4);
+ r = abi.encodeWithSignature(x, y);
+ // The hash uses temporary memory. This allocation re-uses the memory
+ // and should initialize it properly.
+ ar = new uint[](2);
+ }
+ }
+ )T";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b"));
+ bytes expectation;
+ expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f1()"), expectation);
+ ABI_CHECK(callContractFunction("f1s()"), expectation);
+ expectation =
+ encodeArgs(0x40, 0x140, 4 + 0xc0) +
+ (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) +
+ encodeArgs(2, 0, 0);
+ ABI_CHECK(callContractFunction("f2()"), expectation);
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2)
+{
+ char const* sourceCode = R"T(
+ pragma experimental ABIEncoderV2;
+ contract C {
+ function f0() public pure returns (bytes) {
+ return abi.encodeWithSignature("f(uint256)");
+ }
+ function f1() public pure returns (bytes) {
+ string memory x = "f(uint256)";
+ return abi.encodeWithSignature(x, "abc");
+ }
+ string xstor;
+ function f1s() public returns (bytes) {
+ xstor = "f(uint256)";
+ return abi.encodeWithSignature(xstor, "abc");
+ }
+ function f2() public pure returns (bytes r, uint[] ar) {
+ string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
+ uint[] memory y = new uint[](4);
+ y[0] = uint(-1);
+ y[1] = uint(-2);
+ y[2] = uint(-3);
+ y[3] = uint(-4);
+ r = abi.encodeWithSignature(x, y);
+ // The hash uses temporary memory. This allocation re-uses the memory
+ // and should initialize it properly.
+ ar = new uint[](2);
+ }
+ struct S { uint a; string b; uint16 c; }
+ function f4() public pure returns (bytes) {
+ bytes4 x = 0x12345678;
+ S memory s;
+ s.a = 0x1234567;
+ s.b = "Lorem ipsum dolor sit ethereum........";
+ s.c = 0x1234;
+ return abi.encodeWithSignature(s.b, uint(-1), s, uint(3));
+ }
+ }
+ )T";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b"));
+ bytes expectation;
+ expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f1()"), expectation);
+ ABI_CHECK(callContractFunction("f1s()"), expectation);
+ expectation =
+ encodeArgs(0x40, 0x140, 4 + 0xc0) +
+ (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) +
+ encodeArgs(2, 0, 0);
+ ABI_CHECK(callContractFunction("f2()"), expectation);
+ expectation =
+ encodeArgs(0x20, 4 + 0x120) +
+ bytes{0x7c, 0x79, 0x30, 0x02} +
+ encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") +
+ bytes(0x20 - 4);
+ ABI_CHECK(callContractFunction("f4()"), expectation);
+}
+
+BOOST_AUTO_TEST_CASE(abi_encode_call)
+{
+ char const* sourceCode = R"T(
+ contract C {
+ bool x;
+ function c(uint a, uint[] b) public {
+ require(a == 5);
+ require(b.length == 2);
+ require(b[0] == 6);
+ require(b[1] == 7);
+ x = true;
+ }
+ function f() public returns (bool) {
+ uint a = 5;
+ uint[] memory b = new uint[](2);
+ b[0] = 6;
+ b[1] = 7;
+ require(this.call(abi.encodeWithSignature("c(uint256,uint256[])", a, b)));
+ return x;
+ }
+ }
+ )T";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(true));
+}
+
BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure)
{
char const* sourceCode = R"(
@@ -10830,6 +11708,179 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure)
}
}
+BOOST_AUTO_TEST_CASE(swap_peephole_optimisation)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function lt(uint a, uint b) returns (bool c) {
+ assembly {
+ a
+ b
+ swap1
+ lt
+ =: c
+ }
+ }
+ function add(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ swap1
+ add
+ =: c
+ }
+ }
+ function div(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ swap1
+ div
+ =: c
+ }
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(3)));
+ BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(100), u256(200)) == encodeArgs(u256(300)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(2)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(200), u256(10)) == encodeArgs(u256(20)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(1), u256(0)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(0), u256(1)) == encodeArgs(u256(0)));
+}
+
+BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople)
+{
+ if (!dev::test::Options::get().evmVersion().hasBitwiseShifting())
+ return;
+ char const* sourceCode = R"(
+ contract C {
+ function shl(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ shl
+ =: c
+ }
+ }
+ function shr(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ shr
+ =: c
+ }
+ }
+ function sar(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ sar
+ =: c
+ }
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(4)));
+ BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")));
+ BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")));
+ BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")));
+ BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")));
+ BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")));
+}
+
+BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople)
+{
+ if (!dev::test::Options::get().evmVersion().hasBitwiseShifting())
+ return;
+ char const* sourceCode = R"(
+ contract C {
+ function shl_1() returns (bool) {
+ uint c;
+ assembly {
+ 1
+ 2
+ shl
+ =: c
+ }
+ assert(c == 4);
+ return true;
+ }
+ function shl_2() returns (bool) {
+ uint c;
+ assembly {
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ 1
+ shl
+ =: c
+ }
+ assert(c == 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe);
+ return true;
+ }
+ function shl_3() returns (bool) {
+ uint c;
+ assembly {
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ 256
+ shl
+ =: c
+ }
+ assert(c == 0);
+ return true;
+ }
+ function shr_1() returns (bool) {
+ uint c;
+ assembly {
+ 3
+ 1
+ shr
+ =: c
+ }
+ assert(c == 1);
+ return true;
+ }
+ function shr_2() returns (bool) {
+ uint c;
+ assembly {
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ 1
+ shr
+ =: c
+ }
+ assert(c == 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
+ return true;
+ }
+ function shr_3() returns (bool) {
+ uint c;
+ assembly {
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ 256
+ shr
+ =: c
+ }
+ assert(c == 0);
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("shl_1()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shl_2()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shl_3()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shr_1()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shr_2()") == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("shr_3()") == encodeArgs(u256(1)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
index 5f044b44..90d8265c 100644
--- a/test/libsolidity/SolidityExpressionCompiler.cpp
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -30,7 +30,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
-#include "../TestHelper.h"
+#include <test/Options.h>
using namespace std;
@@ -503,12 +503,15 @@ BOOST_AUTO_TEST_CASE(blockhash)
char const* sourceCode = R"(
contract test {
function f() {
- block.blockhash(3);
+ blockhash(3);
}
}
)";
- bytes code = compileFirstExpression(sourceCode, {}, {},
- {make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block))});
+
+ auto blockhashFun = make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"},
+ FunctionType::Kind::BlockHash, false, StateMutability::View);
+
+ bytes code = compileFirstExpression(sourceCode, {}, {}, {make_shared<MagicVariableDeclaration>("blockhash", blockhashFun)});
bytes expectation({byte(Instruction::PUSH1), 0x03,
byte(Instruction::BLOCKHASH)});
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 997b610e..d438a9dc 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -22,7 +22,7 @@
#include <test/libsolidity/AnalysisFramework.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <libsolidity/ast/AST.h>
@@ -43,281 +43,6 @@ namespace test
BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework)
-BOOST_AUTO_TEST_CASE(smoke_test)
-{
- char const* text = R"(
- contract test {
- uint256 stateVariable1;
- function fun(uint256 arg1) public { uint256 y; y = arg1; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
-{
- char const* text = R"(
- contract test {
- uint256 variable;
- uint128 variable;
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
-}
-
-BOOST_AUTO_TEST_CASE(double_function_declaration)
-{
- char const* text = R"(
- contract test {
- function fun() public { }
- function fun() public { }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice.");
-}
-
-BOOST_AUTO_TEST_CASE(double_variable_declaration)
-{
- string text = R"(
- contract test {
- function f() pure public {
- uint256 x;
- if (true) { uint256 x; }
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Identifier already declared");
-}
-
-BOOST_AUTO_TEST_CASE(double_variable_declaration_050)
-{
- string text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- uint256 x;
- if (true) { uint256 x; }
- }
- }
- )";
- CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "This declaration shadows an existing declaration.",
- "Unused local variable",
- "Unused local variable"
- }));
-}
-
-BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope)
-{
- string text = R"(
- contract test {
- function f() pure public {
- { uint x; }
- { uint x; }
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Identifier already declared");
-}
-
-BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050)
-{
- string text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- { uint x; }
- { uint x; }
- }
- }
- )";
- CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "Unused local variable",
- "Unused local variable"
- }));
-}
-
-BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation)
-{
- string text = R"(
- contract test {
- function f() pure public {
- { uint x; }
- uint x;
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Identifier already declared");
-}
-
-BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050)
-{
- string text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- { uint x; }
- uint x;
- }
- }
- )";
- CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "Unused local variable",
- "Unused local variable"
- }));
-}
-BOOST_AUTO_TEST_CASE(scoping_old)
-{
- char const* text = R"(
- contract test {
- function f() pure public {
- x = 4;
- uint256 x = 2;
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(scoping)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() public {
- {
- uint256 x;
- }
- x = 2;
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
-}
-
-BOOST_AUTO_TEST_CASE(scoping_activation_old)
-{
- char const* text = R"(
- contract test {
- function f() pure public {
- x = 3;
- uint x;
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(scoping_activation)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- x = 3;
- uint x;
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
-}
-
-BOOST_AUTO_TEST_CASE(scoping_self_use)
-{
- char const* text = R"(
- contract test {
- function f() pure public {
- uint a = a;
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(scoping_self_use_050)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- uint a = a;
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
-}
-
-BOOST_AUTO_TEST_CASE(scoping_for)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- for (uint x = 0; x < 10; x ++){
- x = 2;
- }
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(scoping_for2)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- for (uint x = 0; x < 10; x ++)
- x = 2;
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(scoping_for3)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- for (uint x = 0; x < 10; x ++){
- x = 2;
- }
- x = 4;
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
-}
-
-BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract test {
- function f() pure public {
- for (;; y++){
- uint y = 3;
- }
- }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
-}
-
-BOOST_AUTO_TEST_CASE(name_shadowing)
-{
- char const* text = R"(
- contract test {
- uint256 variable;
- function f() public { uint32 variable; variable = 2; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
BOOST_AUTO_TEST_CASE(name_references)
{
char const* text = R"(
@@ -367,61 +92,6 @@ BOOST_AUTO_TEST_CASE(reference_to_later_declaration)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive)
-{
- char const* text = R"(
- contract test {
- struct MyStructName {
- address addr;
- MyStructName x;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Recursive struct definition.");
-}
-
-BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive)
-{
- char const* text = R"(
- contract test {
- struct MyStructName1 {
- address addr;
- uint256 count;
- MyStructName2 x;
- }
- struct MyStructName2 {
- MyStructName1 x;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Recursive struct definition.");
-}
-
-BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive)
-{
- char const* text = R"(
- contract test {
- struct s1 { uint a; }
- struct s2 { s1 x; s1 y; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping)
-{
- char const* text = R"(
- contract test {
- struct MyStructName1 {
- address addr;
- uint256 count;
- mapping(uint => MyStructName1) x;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
BOOST_AUTO_TEST_CASE(type_inference_smoke_test)
{
char const* text = R"(
@@ -1237,6 +907,35 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
CHECK_SUCCESS(text);
}
+BOOST_AUTO_TEST_CASE(new_constructor_syntax)
+{
+ char const* text = R"(
+ contract A { constructor() public {} }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(old_constructor_syntax)
+{
+ char const* text = R"(
+ contract A { function A() public {} }
+ )";
+ CHECK_WARNING(
+ text,
+ "Defining constructors as functions with the same name as the contract is deprecated."
+ );
+
+ text = R"(
+ pragma experimental "v0.5.0";
+ contract A { function A() public {} }
+ )";
+ CHECK_ERROR(
+ text,
+ SyntaxError,
+ "Functions are not allowed to have the same name as the contract."
+ );
+}
+
BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)
{
char const* text = R"(
@@ -1276,139 +975,6 @@ BOOST_AUTO_TEST_CASE(super_excludes_current_contract)
CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract super B");
}
-BOOST_AUTO_TEST_CASE(function_modifier_invocation)
-{
- char const* text = R"(
- contract B {
- function f() mod1(2, true) mod2("0123456") pure public { }
- modifier mod1(uint a, bool b) { if (b) _; }
- modifier mod2(bytes7 a) { while (a == "1234567") _; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(invalid_function_modifier_type)
-{
- char const* text = R"(
- contract B {
- function f() mod1(true) public { }
- modifier mod1(uint a) { if (a > 0) _; }
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested.");
-}
-
-BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters)
-{
- char const* text = R"(
- contract B {
- function f(uint8 a) mod1(a, true) mod2(r) public returns (bytes7 r) { }
- modifier mod1(uint a, bool b) { if (b) _; }
- modifier mod2(bytes7 a) { while (a == "1234567") _; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables)
-{
- char const* text = R"(
- contract B {
- function f() mod(x) pure public { uint x = 7; }
- modifier mod(uint a) { if (a > 0) _; }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
-}
-
-BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- contract B {
- function f() mod(x) pure public { uint x = 7; }
- modifier mod(uint a) { if (a > 0) _; }
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Undeclared identifier.");
-}
-
-BOOST_AUTO_TEST_CASE(function_modifier_double_invocation)
-{
- char const* text = R"(
- contract B {
- function f(uint x) mod(x) mod(2) public { }
- modifier mod(uint a) { if (a > 0) _; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(base_constructor_double_invocation)
-{
- char const* text = R"(
- contract C { function C(uint a) public {} }
- contract B is C {
- function B() C(2) C(2) public {}
- }
- )";
- CHECK_ERROR(text, DeclarationError, "Base constructor already provided");
-}
-
-BOOST_AUTO_TEST_CASE(legal_modifier_override)
-{
- char const* text = R"(
- contract A { modifier mod(uint a) { _; } }
- contract B is A { modifier mod(uint a) { _; } }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(illegal_modifier_override)
-{
- char const* text = R"(
- contract A { modifier mod(uint a) { _; } }
- contract B is A { modifier mod(uint8 a) { _; } }
- )";
- CHECK_ERROR(text, TypeError, "Override changes modifier signature.");
-}
-
-BOOST_AUTO_TEST_CASE(modifier_overrides_function)
-{
- char const* text = R"(
- contract A { modifier mod(uint a) { _; } }
- contract B is A { function mod(uint a) public { } }
- )";
- CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, string>>{
- {Error::Type::DeclarationError, "Identifier already declared"},
- {Error::Type::TypeError, "Override changes modifier to function"}
- }));
-}
-
-BOOST_AUTO_TEST_CASE(function_overrides_modifier)
-{
- char const* text = R"(
- contract A { function mod(uint a) public { } }
- contract B is A { modifier mod(uint a) { _; } }
- )";
- CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, string>>{
- {Error::Type::DeclarationError, "Identifier already declared"},
- {Error::Type::TypeError, "Override changes function to modifier"}
- }));
-}
-
-BOOST_AUTO_TEST_CASE(modifier_returns_value)
-{
- char const* text = R"(
- contract A {
- function f(uint a) mod(2) public returns (uint r) { }
- modifier mod(uint a) { _; return 7; }
- }
- )";
- CHECK_ERROR(text, TypeError, "Return arguments not allowed.");
-}
-
BOOST_AUTO_TEST_CASE(state_variable_accessors)
{
char const* text = R"(
@@ -2727,8 +2293,8 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName)
BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared<FixedBytesType>(31));
BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared<FixedBytesType>(32));
- BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed));
- BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned));
}
BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1)
@@ -3424,7 +2990,10 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible)
}
}
)";
- CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\".");
+ if (dev::test::Options::get().evmVersion() == EVMVersion::homestead())
+ CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\".");
+ else
+ CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated");
}
BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable)
@@ -4474,21 +4043,6 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(constructor_call_invalid_arg_count)
-{
- // This caused a segfault in an earlier version
- char const* text = R"(
- contract C {
- function C(){}
- }
- contract D is C {
- function D() C(5){}
- }
- )";
-
- CHECK_ERROR(text, TypeError, "Wrong argument count for modifier invocation: 1 arguments given but expected 0.");
-}
-
BOOST_AUTO_TEST_CASE(index_access_for_bytes)
{
char const* text = R"(
@@ -4743,7 +4297,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed)
}
}
)";
- CHECK_ERROR(text, TypeError, "Type fixed128x19 is not implicitly convertible to expected type int256");
+ CHECK_ERROR(text, TypeError, "Type fixed128x18 is not implicitly convertible to expected type int256");
}
BOOST_AUTO_TEST_CASE(rational_unary_operation)
@@ -4861,7 +4415,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size)
}
}
)";
- CHECK_ERROR(text, TypeError, "Type ufixed128x19 is not implicitly convertible to expected type ufixed248x8");
+ CHECK_ERROR(text, TypeError, "Type ufixed128x18 is not implicitly convertible to expected type ufixed248x8");
}
BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data)
@@ -4948,7 +4502,7 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion)
}
}
)";
- CHECK_ERROR(text, TypeError, "fixed128x19 is not implicitly convertible to expected type bytes32");
+ CHECK_ERROR(text, TypeError, "fixed128x18 is not implicitly convertible to expected type bytes32");
}
BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal)
@@ -5218,20 +4772,6 @@ BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(signed_rational_modulus)
-{
- char const* text = R"(
- contract test {
- function f() public {
- fixed a = 0.42578125 % -0.4271087646484375;
- fixed b = .5 % a;
- fixed c = a % b;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion)
{
char const* text = R"(
@@ -5353,16 +4893,6 @@ BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function)
CHECK_SUCCESS_NO_WARNINGS(text);
}
-BOOST_AUTO_TEST_CASE(modifier_without_underscore)
-{
- char const* text = R"(
- contract test {
- modifier m() {}
- }
- )";
- CHECK_ERROR(text, SyntaxError, "Modifier body does not contain '_'.");
-}
-
BOOST_AUTO_TEST_CASE(payable_in_library)
{
char const* text = R"(
@@ -5562,319 +5092,6 @@ BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype)
CHECK_ERROR(text, TypeError, "Member \"b\" not found or not visible after argument-dependent lookup in bytes memory");
}
-BOOST_AUTO_TEST_CASE(function_type)
-{
- char const* text = R"(
- contract C {
- function f() public {
- function(uint) returns (uint) x;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(function_type_parameter)
-{
- char const* text = R"(
- contract C {
- function f(function(uint) external returns (uint) g) public returns (function(uint) external returns (uint)) {
- return g;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(function_type_returned)
-{
- char const* text = R"(
- contract C {
- function f() public returns (function(uint) external returns (uint) g) {
- return g;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(private_function_type)
-{
- char const* text = R"(
- contract C {
- function f() public {
- function(uint) private returns (uint) x;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\".");
-}
-
-BOOST_AUTO_TEST_CASE(public_function_type)
-{
- char const* text = R"(
- contract C {
- function f() public {
- function(uint) public returns (uint) x;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\".");
-}
-
-BOOST_AUTO_TEST_CASE(payable_internal_function_type)
-{
- char const* text = R"(
- contract C {
- function (uint) internal payable returns (uint) x;
- }
- )";
- CHECK_ERROR(text, TypeError, "Only external function types can be payable.");
-}
-
-BOOST_AUTO_TEST_CASE(payable_internal_function_type_is_not_fatal)
-{
- char const* text = R"(
- contract C {
- function (uint) internal payable returns (uint) x;
-
- function g() {
- x = g;
- }
- }
- )";
- CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{"Only external function types can be payable."}));
-}
-
-BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type)
-{
- char const* text = R"(
- contract C {
- function (uint) external returns (uint) x;
- function f() public {
- x.value(2)();
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function (uint256) external returns (uint256) - did you forget the \"payable\" modifier?");
-}
-
-BOOST_AUTO_TEST_CASE(external_function_type_returning_internal)
-{
- char const* text = R"(
- contract C {
- function() external returns (function () internal) x;
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type.");
-}
-
-BOOST_AUTO_TEST_CASE(external_function_type_taking_internal)
-{
- char const* text = R"(
- contract C {
- function(function () internal) external x;
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type.");
-}
-
-BOOST_AUTO_TEST_CASE(call_value_on_payable_function_type)
-{
- char const* text = R"(
- contract C {
- function (uint) external payable returns (uint) x;
- function f() public {
- x.value(2)(1);
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter)
-{
- // It should not be possible to give internal functions
- // as parameters to external functions.
- char const* text = R"(
- contract C {
- function f(function(uint) internal returns (uint) x) public {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
-{
- // It should not be possible to return internal functions from external functions.
- char const* text = R"(
- contract C {
- function f() public returns (function(uint) internal returns (uint) x) {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal)
-{
- char const* text = R"(
- library L {
- function f(function(uint) internal returns (uint) x) internal {
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external)
-{
- char const* text = R"(
- library L {
- function f(function(uint) internal returns (uint) x) public {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(function_type_arrays)
-{
- char const* text = R"(
- contract C {
- function(uint) external returns (uint)[] public x;
- function(uint) internal returns (uint)[10] y;
- function f() public {
- function(uint) returns (uint)[10] memory a;
- function(uint) returns (uint)[10] storage b = y;
- function(uint) external returns (uint)[] memory c;
- c = new function(uint) external returns (uint)[](200);
- a; b;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(delete_function_type)
-{
- char const* text = R"(
- contract C {
- function(uint) external returns (uint) x;
- function(uint) internal returns (uint) y;
- function f() public {
- delete x;
- var a = y;
- delete a;
- delete y;
- var c = f;
- delete c;
- function(uint) internal returns (uint) g;
- delete g;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(delete_function_type_invalid)
-{
- char const* text = R"(
- contract C {
- function f() public {
- delete f;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Expression has to be an lvalue.");
-}
-
-BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid)
-{
- char const* text = R"(
- contract C {
- function f() public {
- delete this.f;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Expression has to be an lvalue.");
-}
-
-BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter)
-{
- // This is a test that checks that the type of the `bytes` parameter is
- // correctly changed from its own type `bytes calldata` to `bytes memory`
- // when converting to a function type.
- char const* text = R"(
- contract C {
- function f(function(bytes memory) external g) public { }
- function callback(bytes) external {}
- function g() public {
- f(this.callback);
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(external_function_type_to_address)
-{
- char const* text = R"(
- contract C {
- function f() public returns (address) {
- return address(this.f);
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(internal_function_type_to_address)
-{
- char const* text = R"(
- contract C {
- function f() public returns (address) {
- return address(f);
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed");
-}
-
-BOOST_AUTO_TEST_CASE(external_function_type_to_uint)
-{
- char const* text = R"(
- contract C {
- function f() public returns (uint) {
- return uint(this.f);
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed");
-}
-
-BOOST_AUTO_TEST_CASE(warn_function_type_parameters_with_names)
-{
- char const* text = R"(
- contract C {
- function(uint a) f;
- }
- )";
- CHECK_WARNING(text, "Naming function type parameters is deprecated.");
-}
-
-BOOST_AUTO_TEST_CASE(warn_function_type_return_parameters_with_names)
-{
- char const* text = R"(
- contract C {
- function(uint) returns (bool ret) f;
- }
- )";
- CHECK_WARNING(text, "Naming function type return parameters is deprecated.");
-}
-
BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue)
{
char const* text = R"(
@@ -5915,6 +5132,16 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue)
CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const 4294967296");
}
+BOOST_AUTO_TEST_CASE(shift_constant_right_fractional)
+{
+ char const* text = R"(
+ contract C {
+ uint public a = 0x42 >> (1 / 2);
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and rational_const 1 / 2");
+}
+
BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack)
{
char const* text = R"(
@@ -6439,43 +5666,22 @@ BOOST_AUTO_TEST_CASE(return_structs)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(return_recursive_structs)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; S[] sub; }
- function f() returns (uint, S) {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(return_recursive_structs2)
+BOOST_AUTO_TEST_CASE(read_returned_struct)
{
char const* text = R"(
- contract C {
- struct S { uint a; S[2][] sub; }
- function f() returns (uint, S) {
+ pragma experimental ABIEncoderV2;
+ contract A {
+ struct T {
+ int x;
+ int y;
}
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(return_recursive_structs3)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; S[][][] sub; }
- struct T { S s; }
- function f() returns (uint x, T t) {
+ function g() public returns (T) {
+ return this.g();
}
}
)";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
+ CHECK_WARNING(text, "Experimental features");
}
-
BOOST_AUTO_TEST_CASE(address_checksum_type_deduction)
{
char const* text = R"(
@@ -6598,38 +5804,6 @@ BOOST_AUTO_TEST_CASE(address_methods)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants)
-{
- char const* text = R"(
- contract C {
- uint constant a = a;
- }
- )";
- CHECK_ERROR(text, TypeError, "cyclic dependency via a");
- text = R"(
- contract C {
- uint constant a = b * c;
- uint constant b = 7;
- uint constant c = b + uint(keccak256(d));
- uint constant d = 2 + a;
- }
- )";
- CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{
- "a has a cyclic dependency via c",
- "c has a cyclic dependency via d",
- "d has a cyclic dependency via a"
- }));
- text = R"(
- contract C {
- uint constant a = b * c;
- uint constant b = 7;
- uint constant c = 4 + uint(keccak256(d));
- uint constant d = 2 + b;
- }
- )";
- CHECK_SUCCESS(text);
-}
-
BOOST_AUTO_TEST_CASE(interface)
{
char const* text = R"(
@@ -6674,54 +5848,6 @@ BOOST_AUTO_TEST_CASE(interface_function_bodies)
CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation");
}
-BOOST_AUTO_TEST_CASE(interface_function_external)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- interface I {
- function f() external;
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(interface_function_public)
-{
- char const* text = R"(
- interface I {
- function f() public;
- }
- )";
- CHECK_WARNING(text, "Functions in interfaces should be declared external.");
- text = R"(
- pragma experimental "v0.5.0";
- interface I {
- function f() public;
- }
- )";
- CHECK_ERROR(text, TypeError, "Functions in interfaces must be declared external.");
-}
-
-BOOST_AUTO_TEST_CASE(interface_function_internal)
-{
- char const* text = R"(
- interface I {
- function f() internal;
- }
- )";
- CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private.");
-}
-
-BOOST_AUTO_TEST_CASE(interface_function_private)
-{
- char const* text = R"(
- interface I {
- function f() private;
- }
- )";
- CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private.");
-}
-
BOOST_AUTO_TEST_CASE(interface_events)
{
char const* text = R"(
@@ -6816,6 +5942,20 @@ BOOST_AUTO_TEST_CASE(using_interface_complex)
CHECK_SUCCESS(text);
}
+BOOST_AUTO_TEST_CASE(interface_implement_public_contract)
+{
+ char const* text = R"(
+ interface I {
+ function f() external;
+ }
+ contract C is I {
+ function f() public {
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
BOOST_AUTO_TEST_CASE(warn_about_throw)
{
char const* text = R"(
@@ -6847,14 +5987,30 @@ BOOST_AUTO_TEST_CASE(bare_revert)
}
}
)";
- CHECK_WARNING(text, "Statement has no effect.");
+ CHECK_ERROR(text, TypeError, "No matching declaration found");
+}
+
+BOOST_AUTO_TEST_CASE(revert_with_reason)
+{
+ char const* text = R"(
+ contract C {
+ function f(uint x) pure public {
+ if (x > 7)
+ revert("abc");
+ else
+ revert();
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(bare_others)
{
CHECK_WARNING("contract C { function f() pure public { selfdestruct; } }", "Statement has no effect.");
CHECK_WARNING("contract C { function f() pure public { assert; } }", "Statement has no effect.");
- CHECK_WARNING("contract C { function f() pure public { require; } }", "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.");
}
@@ -7195,7 +6351,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor)
{
char const* text = R"(
contract C {
- function C() public {}
+ constructor() public {}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
@@ -7353,7 +6509,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function)
CHECK_SUCCESS_NO_WARNINGS(text);
}
-BOOST_AUTO_TEST_CASE(returndatacopy_as_variable)
+BOOST_AUTO_TEST_CASE(returndatasize_as_variable)
{
char const* text = R"(
contract c { function f() public { uint returndatasize; assembly { returndatasize }}}
@@ -7520,7 +6676,7 @@ BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer)
{
char const* text = R"(
contract C {
- struct S { }
+ struct S { uint a; }
modifier m(S storage x) {
x;
_;
@@ -7607,7 +6763,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor)
{
char const* text = R"(
contract C {
- function C() public {
+ constructor() public {
this.f();
}
function f() pure public {
@@ -7921,53 +7077,6 @@ BOOST_AUTO_TEST_CASE(reject_interface_constructors)
CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0.");
}
-BOOST_AUTO_TEST_CASE(tight_packing_literals)
-{
- char const* text = R"(
- contract C {
- function f() pure public returns (bytes32) {
- return keccak256(1);
- }
- }
- )";
- CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8.");
- text = R"(
- contract C {
- function f() pure public returns (bytes32) {
- return keccak256(uint8(1));
- }
- }
- )";
- CHECK_SUCCESS_NO_WARNINGS(text);
- text = R"(
- contract C {
- function f() pure public returns (bytes32) {
- return sha3(1);
- }
- }
- )";
- CHECK_WARNING_ALLOW_MULTI(text, (std::vector<std::string>{
- "The type of \"int_const 1\" was inferred as uint8.",
- "\"sha3\" has been deprecated in favour of \"keccak256\""
- }));
- text = R"(
- contract C {
- function f() pure public returns (bytes32) {
- return sha256(1);
- }
- }
- )";
- CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8.");
- text = R"(
- contract C {
- function f() pure public returns (bytes32) {
- return ripemd160(1);
- }
- }
- )";
- CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8.");
-}
-
BOOST_AUTO_TEST_CASE(non_external_fallback)
{
char const* text = R"(
@@ -8095,171 +7204,6 @@ BOOST_AUTO_TEST_CASE(address_overload_resolution)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(array_length_too_large)
-{
- char const* text = R"(
- contract C {
- uint[8**90] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer)
-{
- char const* text = R"(
- contract C {
- uint[true] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_constant_var)
-{
- char const* text = R"(
- contract C {
- uint constant LEN = 10;
- uint[LEN] ids;
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(array_length_non_integer_constant_var)
-{
- char const* text = R"(
- contract C {
- bool constant LEN = true;
- uint[LEN] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_cannot_be_function)
-{
- char const* text = R"(
- contract C {
- function f() {}
- uint[f] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_can_be_recursive_constant)
-{
- char const* text = R"(
- contract C {
- uint constant L = 5;
- uint constant LEN = L + 4 * L;
- uint[LEN] ids;
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(array_length_cannot_be_function_call)
-{
- char const* text = R"(
- contract C {
- function f(uint x) {}
- uint constant LEN = f();
- uint[LEN] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_const_cannot_be_fractional)
-{
- char const* text = R"(
- contract C {
- fixed constant L = 10.5;
- uint[L] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Array with fractional length specified");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_can_be_constant_in_struct)
-{
- char const* text = R"(
- contract C {
- uint constant LEN = 10;
- struct Test {
- uint[LEN] ids;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(array_length_can_be_constant_in_function)
-{
- char const* text = R"(
- contract C {
- uint constant LEN = 10;
- function f() {
- uint[LEN] a;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(array_length_cannot_be_constant_function_parameter)
-{
- char const* text = R"(
- contract C {
- function f(uint constant LEN) {
- uint[LEN] a;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_with_cyclic_constant)
-{
- char const* text = R"(
- contract C {
- uint constant LEN = LEN;
- function f() {
- uint[LEN] a;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Cyclic constant definition (or maximum recursion depth exhausted).");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_with_complex_cyclic_constant)
-{
- char const* text = R"(
- contract C {
- uint constant L2 = LEN - 10;
- uint constant L1 = L2 / 10;
- uint constant LEN = 10 + L1 * 5;
- function f() {
- uint[LEN] a;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Cyclic constant definition (or maximum recursion depth exhausted).");
-}
-
-BOOST_AUTO_TEST_CASE(array_length_with_pure_functions)
-{
- char const* text = R"(
- contract C {
- uint constant LEN = keccak256(ripemd160(33));
- uint[LEN] ids;
- }
- )";
- CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression.");
-}
-
BOOST_AUTO_TEST_CASE(array_length_invalid_expression)
{
char const* text = R"(
@@ -8587,6 +7531,33 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers)
CHECK_ERROR(text, SyntaxError, "No visibility specified.");
}
+BOOST_AUTO_TEST_CASE(blockhash)
+{
+ char const* code = R"(
+ contract C {
+ function f() public view returns (bytes32) {
+ return block.blockhash(3);
+ }
+ }
+ )";
+ CHECK_WARNING(code, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"");
+
+ code = R"(
+ contract C {
+ function f() public view returns (bytes32) { return blockhash(3); }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(code);
+
+ code = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() public returns (bytes32) { return block.blockhash(3); }
+ }
+ )";
+ CHECK_ERROR(code, TypeError, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"");
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp
index 49a725e0..eeebeb74 100644
--- a/test/libsolidity/SolidityNatspecJSON.cpp
+++ b/test/libsolidity/SolidityNatspecJSON.cpp
@@ -20,7 +20,7 @@
* Unit tests for the solidity compiler JSON Interface output.
*/
-#include "../TestHelper.h"
+#include <test/Options.h>
#include <string>
#include <libdevcore/JSON.h>
#include <libsolidity/interface/CompilerStack.h>
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
index 33039ca9..5326feaf 100644
--- a/test/libsolidity/SolidityOptimizer.cpp
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -74,11 +74,11 @@ public:
unsigned const _optimizeRuns = 200
)
{
- bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns);
+ m_nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns);
m_nonOptimizedContract = m_contractAddress;
- bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns);
- size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode);
- size_t optimizedSize = numInstructions(optimizedBytecode);
+ m_optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns);
+ size_t nonOptimizedSize = numInstructions(m_nonOptimizedBytecode);
+ size_t optimizedSize = numInstructions(m_optimizedBytecode);
BOOST_CHECK_MESSAGE(
_optimizeRuns < 50 || optimizedSize < nonOptimizedSize,
string("Optimizer did not reduce bytecode size. Non-optimized size: ") +
@@ -93,8 +93,10 @@ public:
{
m_contractAddress = m_nonOptimizedContract;
bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...);
+ m_gasUsedNonOptimized = m_gasUsed;
m_contractAddress = m_optimizedContract;
bytes optimizedOutput = callContractFunction(_sig, _arguments...);
+ m_gasUsedOptimized = m_gasUsed;
BOOST_CHECK_MESSAGE(!optimizedOutput.empty(), "No optimized output for " + _sig);
BOOST_CHECK_MESSAGE(!nonOptimizedOutput.empty(), "No un-optimized output for " + _sig);
BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match."
@@ -104,7 +106,7 @@ public:
/// @returns the number of intructions in the given bytecode, not taking the metadata hash
/// into account.
- size_t numInstructions(bytes const& _bytecode)
+ size_t numInstructions(bytes const& _bytecode, boost::optional<Instruction> _which = boost::optional<Instruction>{})
{
BOOST_REQUIRE(_bytecode.size() > 5);
size_t metadataSize = (_bytecode[_bytecode.size() - 2] << 8) + _bytecode[_bytecode.size() - 1];
@@ -112,13 +114,18 @@ public:
BOOST_REQUIRE(_bytecode.size() >= metadataSize + 2);
bytes realCode = bytes(_bytecode.begin(), _bytecode.end() - metadataSize - 2);
size_t instructions = 0;
- solidity::eachInstruction(realCode, [&](Instruction, u256 const&) {
- instructions++;
+ solidity::eachInstruction(realCode, [&](Instruction _instr, u256 const&) {
+ if (!_which || *_which == _instr)
+ instructions++;
});
return instructions;
}
protected:
+ u256 m_gasUsedOptimized;
+ u256 m_gasUsedNonOptimized;
+ bytes m_nonOptimizedBytecode;
+ bytes m_optimizedBytecode;
Address m_optimizedContract;
Address m_nonOptimizedContract;
};
@@ -581,6 +588,49 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join)
compareVersions("test()");
}
+BOOST_AUTO_TEST_CASE(init_empty_dynamic_arrays)
+{
+ // This is not so much an optimizer test, but rather a test
+ // that allocating empty arrays is implemented efficiently.
+ // In particular, initializing a dynamic memory array does
+ // not use any memory.
+ char const* sourceCode = R"(
+ contract Test {
+ function f() pure returns (uint r) {
+ uint[][] memory x = new uint[][](20000);
+ return x.length;
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f()");
+ BOOST_CHECK_LE(m_gasUsedNonOptimized, 1900000);
+ BOOST_CHECK_LE(1600000, m_gasUsedNonOptimized);
+}
+
+BOOST_AUTO_TEST_CASE(optimise_multi_stores)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint16 a; uint16 b; uint16[3] c; uint[] dyn; }
+ uint padding;
+ S[] s;
+ function f() public returns (uint16, uint16, uint16[3], uint) {
+ uint16[3] memory c;
+ c[0] = 7;
+ c[1] = 8;
+ c[2] = 9;
+ s.push(S(1, 2, c, new uint[](4)));
+ return (s[0].a, s[0].b, s[0].c, s[0].dyn[2]);
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f()");
+ BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9);
+ BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 8);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index f03b30e1..100b3662 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -25,8 +25,8 @@
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/interface/ErrorReporter.h>
-#include "../TestHelper.h"
-#include "ErrorCheck.h"
+#include <test/Options.h>
+#include <test/libsolidity/ErrorCheck.h>
using namespace std;
@@ -112,26 +112,6 @@ while(0)
BOOST_AUTO_TEST_SUITE(SolidityParser)
-BOOST_AUTO_TEST_CASE(smoke_test)
-{
- char const* text = R"(
- contract test {
- uint256 stateVariable1;
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration)
-{
- char const* text = R"(
- contract test {
- uint256 ;
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
BOOST_AUTO_TEST_CASE(empty_function)
{
char const* text = R"(
@@ -577,16 +557,6 @@ BOOST_AUTO_TEST_CASE(variable_definition_with_initialization)
BOOST_CHECK(successParse(text));
}
-BOOST_AUTO_TEST_CASE(variable_definition_in_function_parameter)
-{
- char const* text = R"(
- contract test {
- function fun(var a) {}
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected explicit type name");
-}
-
BOOST_AUTO_TEST_CASE(variable_definition_in_mapping)
{
char const* text = R"(
@@ -599,18 +569,6 @@ BOOST_AUTO_TEST_CASE(variable_definition_in_mapping)
CHECK_PARSE_ERROR(text, "Expected elementary type name for mapping key type");
}
-BOOST_AUTO_TEST_CASE(variable_definition_in_function_return)
-{
- char const* text = R"(
- contract test {
- function fun() returns(var d) {
- return 1;
- }
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected explicit type name");
-}
-
BOOST_AUTO_TEST_CASE(operator_expression)
{
char const* text = R"(
diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp
index bc9f2fe1..738b24bc 100644
--- a/test/libsolidity/SolidityTypes.cpp
+++ b/test/libsolidity/SolidityTypes.cpp
@@ -123,6 +123,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers)
BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes32")->identifier(), "t_bytes32");
BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bool")->identifier(), "t_bool");
BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes")->identifier(), "t_bytes_storage_ptr");
+ BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes memory")->identifier(), "t_bytes_memory_ptr");
BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string")->identifier(), "t_string_storage_ptr");
ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752"));
BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_array$_t_int128_$2535301200456458802993406410752_memory_ptr");
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 4c8918be..74bf01b2 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -261,19 +261,24 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
- "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00"
+ "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00"
);
BOOST_CHECK(contract["evm"]["assembly"].isString());
BOOST_CHECK(contract["evm"]["assembly"].asString().find(
- " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n"
- " 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n"
- " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n"
- " mstore(0x40, 0x60)\n 0x0\n dup1\n revert\n\n"
- " auxdata: 0xa165627a7a7230582") == 0);
+ " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n "
+ "callvalue\n /* \"--CODEGEN--\":8:17 */\n dup1\n "
+ "/* \"--CODEGEN--\":5:7 */\n iszero\n tag_1\n jumpi\n "
+ "/* \"--CODEGEN--\":30:31 */\n 0x0\n /* \"--CODEGEN--\":27:28 */\n "
+ "dup1\n /* \"--CODEGEN--\":20:32 */\n revert\n /* \"--CODEGEN--\":5:7 */\n"
+ "tag_1:\n /* \"fileA\":0:14 contract A { } */\n pop\n dataSize(sub_0)\n dup1\n "
+ "dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n return\nstop\n\nsub_0: assembly {\n "
+ "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n 0x0\n "
+ "dup1\n revert\n\n auxdata: 0xa165627a7a72305820"
+ ) == 0);
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
BOOST_CHECK_EQUAL(
dev::jsonCompactPrint(contract["evm"]["gasEstimates"]),
- "{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"61\",\"totalCost\":\"10661\"}}"
+ "{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"66\",\"totalCost\":\"10666\"}}"
);
BOOST_CHECK(contract["metadata"].isString());
BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString()));
@@ -752,6 +757,8 @@ BOOST_AUTO_TEST_CASE(evm_version)
BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos);
result = compile(inputForVersion("\"evmVersion\": \"byzantium\","));
BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos);
+ result = compile(inputForVersion("\"evmVersion\": \"constantinople\","));
+ BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"constantinople\"") != string::npos);
// test default
result = compile(inputForVersion(""));
BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos);
diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp
new file mode 100644
index 00000000..1c2355d5
--- /dev/null
+++ b/test/libsolidity/SyntaxTest.cpp
@@ -0,0 +1,283 @@
+/*
+ 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 <test/libsolidity/SyntaxTest.h>
+#include <test/Options.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/throw_exception.hpp>
+#include <cctype>
+#include <fstream>
+#include <memory>
+#include <stdexcept>
+
+using namespace dev;
+using namespace solidity;
+using namespace dev::solidity::test;
+using namespace dev::solidity::test::formatting;
+using namespace std;
+namespace fs = boost::filesystem;
+using namespace boost::unit_test;
+
+template<typename IteratorType>
+void skipWhitespace(IteratorType& _it, IteratorType _end)
+{
+ while (_it != _end && isspace(*_it))
+ ++_it;
+}
+
+template<typename IteratorType>
+void skipSlashes(IteratorType& _it, IteratorType _end)
+{
+ while (_it != _end && *_it == '/')
+ ++_it;
+}
+
+void expect(string::iterator& _it, string::iterator _end, string::value_type _c)
+{
+ if (_it == _end || *_it != _c)
+ throw runtime_error(string("Invalid test expectation. Expected: \"") + _c + "\".");
+ ++_it;
+}
+
+int parseUnsignedInteger(string::iterator &_it, string::iterator _end)
+{
+ if (_it == _end || !isdigit(*_it))
+ throw runtime_error("Invalid test expectation. Source location expected.");
+ int result = 0;
+ while (_it != _end && isdigit(*_it))
+ {
+ result *= 10;
+ result += *_it - '0';
+ ++_it;
+ }
+ return result;
+}
+
+SyntaxTest::SyntaxTest(string const& _filename)
+{
+ ifstream file(_filename);
+ if (!file)
+ BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\"."));
+ file.exceptions(ios::badbit);
+
+ m_source = parseSource(file);
+ m_expectations = parseExpectations(file);
+}
+
+bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
+{
+ string const versionPragma = "pragma solidity >=0.0;\n";
+ m_compiler.reset();
+ m_compiler.addSource("", versionPragma + m_source);
+ m_compiler.setEVMVersion(dev::test::Options::get().evmVersion());
+
+ if (m_compiler.parse())
+ m_compiler.analyze();
+
+ for (auto const& currentError: filterErrors(m_compiler.errors(), true))
+ {
+ int locationStart = -1, locationEnd = -1;
+ if (auto location = boost::get_error_info<errinfo_sourceLocation>(*currentError))
+ {
+ // ignore the version pragma inserted by the testing tool when calculating locations.
+ if (location->start >= static_cast<int>(versionPragma.size()))
+ locationStart = location->start - versionPragma.size();
+ if (location->end >= static_cast<int>(versionPragma.size()))
+ locationEnd = location->end - versionPragma.size();
+ }
+ m_errorList.emplace_back(SyntaxTestError{
+ currentError->typeName(),
+ errorMessage(*currentError),
+ locationStart,
+ locationEnd
+ });
+ }
+
+ if (m_expectations != m_errorList)
+ {
+ string nextIndentLevel = _linePrefix + " ";
+ FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
+ printErrorList(_stream, m_expectations, nextIndentLevel, _formatted);
+ FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
+ printErrorList(_stream, m_errorList, nextIndentLevel, _formatted);
+ return false;
+ }
+ return true;
+}
+
+void SyntaxTest::printErrorList(
+ ostream& _stream,
+ vector<SyntaxTestError> const& _errorList,
+ string const& _linePrefix,
+ bool const _formatted
+)
+{
+ if (_errorList.empty())
+ FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl;
+ else
+ for (auto const& error: _errorList)
+ {
+ {
+ FormattedScope scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED});
+ _stream << _linePrefix;
+ _stream << error.type << ": ";
+ }
+ if (error.locationStart >= 0 || error.locationEnd >= 0)
+ {
+ _stream << "(";
+ if (error.locationStart >= 0)
+ _stream << error.locationStart;
+ _stream << "-";
+ if (error.locationEnd >= 0)
+ _stream << error.locationEnd;
+ _stream << "): ";
+ }
+ _stream << error.message << endl;
+ }
+}
+
+string SyntaxTest::errorMessage(Exception const& _e)
+{
+ if (_e.comment() && !_e.comment()->empty())
+ return boost::replace_all_copy(*_e.comment(), "\n", "\\n");
+ else
+ return "NONE";
+}
+
+string SyntaxTest::parseSource(istream& _stream)
+{
+ string source;
+ string line;
+ string const delimiter("// ----");
+ while (getline(_stream, line))
+ if (boost::algorithm::starts_with(line, delimiter))
+ break;
+ else
+ source += line + "\n";
+ return source;
+}
+
+vector<SyntaxTestError> SyntaxTest::parseExpectations(istream& _stream)
+{
+ vector<SyntaxTestError> expectations;
+ string line;
+ while (getline(_stream, line))
+ {
+ auto it = line.begin();
+
+ skipSlashes(it, line.end());
+ skipWhitespace(it, line.end());
+
+ if (it == line.end()) continue;
+
+ auto typeBegin = it;
+ while (it != line.end() && *it != ':')
+ ++it;
+ string errorType(typeBegin, it);
+
+ // skip colon
+ if (it != line.end()) it++;
+
+ skipWhitespace(it, line.end());
+
+ int locationStart = -1;
+ int locationEnd = -1;
+
+ if (it != line.end() && *it == '(')
+ {
+ ++it;
+ locationStart = parseUnsignedInteger(it, line.end());
+ expect(it, line.end(), '-');
+ locationEnd = parseUnsignedInteger(it, line.end());
+ expect(it, line.end(), ')');
+ expect(it, line.end(), ':');
+ }
+
+ skipWhitespace(it, line.end());
+
+ string errorMessage(it, line.end());
+ expectations.emplace_back(SyntaxTestError{
+ move(errorType),
+ move(errorMessage),
+ locationStart,
+ locationEnd
+ });
+ }
+ return expectations;
+}
+
+#if BOOST_VERSION < 105900
+test_case *make_test_case(
+ function<void()> const& _fn,
+ string const& _name,
+ string const& /* _filename */,
+ size_t /* _line */
+)
+{
+ return make_test_case(_fn, _name);
+}
+#endif
+
+bool SyntaxTest::isTestFilename(boost::filesystem::path const& _filename)
+{
+ return _filename.extension().string() == ".sol" &&
+ !boost::starts_with(_filename.string(), "~") &&
+ !boost::starts_with(_filename.string(), ".");
+}
+
+int SyntaxTest::registerTests(
+ boost::unit_test::test_suite& _suite,
+ boost::filesystem::path const& _basepath,
+ boost::filesystem::path const& _path
+)
+{
+ int numTestsAdded = 0;
+ fs::path fullpath = _basepath / _path;
+ if (fs::is_directory(fullpath))
+ {
+ test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
+ for (auto const& entry: boost::iterator_range<fs::directory_iterator>(
+ fs::directory_iterator(fullpath),
+ fs::directory_iterator()
+ ))
+ if (fs::is_directory(entry.path()) || isTestFilename(entry.path().filename()))
+ numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename());
+ _suite.add(sub_suite);
+ }
+ else
+ {
+ static vector<unique_ptr<string>> filenames;
+
+ filenames.emplace_back(new string(_path.string()));
+ _suite.add(make_test_case(
+ [fullpath]
+ {
+ BOOST_REQUIRE_NO_THROW({
+ stringstream errorStream;
+ if (!SyntaxTest(fullpath.string()).run(errorStream))
+ BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str());
+ });
+ },
+ _path.stem().string(),
+ *filenames.back(),
+ 0
+ ));
+ numTestsAdded = 1;
+ }
+ return numTestsAdded;
+}
diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h
new file mode 100644
index 00000000..6159e789
--- /dev/null
+++ b/test/libsolidity/SyntaxTest.h
@@ -0,0 +1,91 @@
+/*
+ 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 <test/libsolidity/AnalysisFramework.h>
+#include <test/libsolidity/FormattedScope.h>
+#include <libsolidity/interface/Exceptions.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+struct SyntaxTestError
+{
+ std::string type;
+ std::string message;
+ int locationStart;
+ int locationEnd;
+ bool operator==(SyntaxTestError const& _rhs) const
+ {
+ return type == _rhs.type &&
+ message == _rhs.message &&
+ locationStart == _rhs.locationStart &&
+ locationEnd == _rhs.locationEnd;
+ }
+};
+
+
+class SyntaxTest: AnalysisFramework
+{
+public:
+ SyntaxTest(std::string const& _filename);
+
+ bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false);
+
+ std::vector<SyntaxTestError> const& expectations() const { return m_expectations; }
+ std::string const& source() const { return m_source; }
+ std::vector<SyntaxTestError> const& errorList() const { return m_errorList; }
+
+ static void printErrorList(
+ std::ostream& _stream,
+ std::vector<SyntaxTestError> const& _errors,
+ std::string const& _linePrefix,
+ bool const _formatted = false
+ );
+
+ static int registerTests(
+ boost::unit_test::test_suite& _suite,
+ boost::filesystem::path const& _basepath,
+ boost::filesystem::path const& _path
+ );
+ static bool isTestFilename(boost::filesystem::path const& _filename);
+ static std::string errorMessage(Exception const& _e);
+private:
+ static std::string parseSource(std::istream& _stream);
+ static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);
+
+ std::string m_source;
+ std::vector<SyntaxTestError> m_expectations;
+ std::vector<SyntaxTestError> m_errorList;
+};
+
+}
+}
+}
diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp
index 26ff461c..cd0a0b01 100644
--- a/test/libsolidity/ViewPureChecker.cpp
+++ b/test/libsolidity/ViewPureChecker.cpp
@@ -20,11 +20,12 @@
#include <test/libsolidity/AnalysisFramework.h>
-#include <test/TestHelper.h>
+#include <test/Options.h>
#include <boost/test/unit_test.hpp>
#include <string>
+#include <tuple>
using namespace std;
@@ -111,6 +112,7 @@ BOOST_AUTO_TEST_CASE(environment_access)
"block.difficulty",
"block.number",
"block.gaslimit",
+ "blockhash(7)",
"gasleft()",
"msg.gas",
"msg.value",
@@ -120,11 +122,12 @@ BOOST_AUTO_TEST_CASE(environment_access)
"this",
"address(1).balance"
};
+ // ``block.blockhash`` and ``blockhash`` are tested seperately below because their usage will
+ // produce warnings that can't be handled in a generic way.
vector<string> pure{
"msg.data",
"msg.data[0]",
"msg.sig",
- "block.blockhash", // Not evaluating the function
"msg",
"block",
"tx"
@@ -132,20 +135,32 @@ BOOST_AUTO_TEST_CASE(environment_access)
for (string const& x: view)
{
CHECK_ERROR(
- "contract C { function f() pure public { var x = " + x + "; x; } }",
+ "contract C { function f() pure public { " + x + "; } }",
TypeError,
"Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\""
);
}
for (string const& x: pure)
{
- CHECK_WARNING_ALLOW_MULTI(
- "contract C { function f() view public { var x = " + x + "; x; } }",
- (std::vector<std::string>{
- "Function state mutability can be restricted to pure",
- "Use of the \"var\" keyword is deprecated."
- }));
+ CHECK_WARNING(
+ "contract C { function f() view public { " + x + "; } }",
+ "Function state mutability can be restricted to pure"
+ );
}
+
+ CHECK_WARNING_ALLOW_MULTI(
+ "contract C { function f() view public { blockhash; } }",
+ (std::vector<std::string>{
+ "Function state mutability can be restricted to pure",
+ "Statement has no effect."
+ }));
+
+ CHECK_WARNING_ALLOW_MULTI(
+ "contract C { function f() view public { block.blockhash; } }",
+ (std::vector<std::string>{
+ "Function state mutability can be restricted to pure",
+ "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
+ }));
}
BOOST_AUTO_TEST_CASE(view_error_for_050)
diff --git a/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol b/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol
new file mode 100644
index 00000000..11d40f26
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f(uint constant LEN) {
+ uint[LEN] a;
+ }
+}
+// ----
+// TypeError: (62-65): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol
new file mode 100644
index 00000000..92536dd5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint constant LEN = 10;
+ function f() public pure {
+ uint[LEN] memory a;
+ a;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol
new file mode 100644
index 00000000..89e174f2
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol
@@ -0,0 +1,7 @@
+contract C {
+ uint constant LEN = 10;
+ struct Test {
+ uint[LEN] ids;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol b/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol
new file mode 100644
index 00000000..6810a9d6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint constant L = 5;
+ uint constant LEN = L + 4 * L;
+ uint[LEN] ids;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol
new file mode 100644
index 00000000..ac3abc4c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol
@@ -0,0 +1,6 @@
+contract C {
+ function f() {}
+ uint[f] ids;
+}
+// ----
+// TypeError: (42-43): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol
new file mode 100644
index 00000000..a6863955
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f(uint x) {}
+ uint constant LEN = f();
+ uint[LEN] ids;
+}
+// ----
+// TypeError: (77-80): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol b/test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol
new file mode 100644
index 00000000..254f9f02
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint constant L2 = LEN - 10;
+ uint constant L1 = L2 / 10;
+ uint constant LEN = 10 + L1 * 5;
+ function f() {
+ uint[LEN] a;
+ }
+}
+// ----
+// TypeError: (36-39): Cyclic constant definition (or maximum recursion depth exhausted).
diff --git a/test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol b/test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol
new file mode 100644
index 00000000..397bbbcd
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol
@@ -0,0 +1,6 @@
+contract C {
+ fixed constant L = 10.5;
+ uint[L] ids;
+}
+// ----
+// TypeError: (51-52): Array with fractional length specified.
diff --git a/test/libsolidity/syntaxTests/arrayLength/constant_var.sol b/test/libsolidity/syntaxTests/arrayLength/constant_var.sol
new file mode 100644
index 00000000..41750250
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/constant_var.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant LEN = 10;
+ uint[LEN] ids;
+}
+// ---- \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol b/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol
new file mode 100644
index 00000000..91ba9045
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol
@@ -0,0 +1,8 @@
+contract C {
+ uint constant LEN = LEN;
+ function f() {
+ uint[LEN] a;
+ }
+}
+// ----
+// TypeError: (37-40): Cyclic constant definition (or maximum recursion depth exhausted).
diff --git a/test/libsolidity/syntaxTests/arrayLength/inline_array.sol b/test/libsolidity/syntaxTests/arrayLength/inline_array.sol
new file mode 100644
index 00000000..a30745d3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/inline_array.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[[2]] a15;
+}
+// ----
+// TypeError: (22-25): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol
new file mode 100644
index 00000000..c92861eb
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[-true] ids;
+}
+// ----
+// TypeError: (22-27): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol
new file mode 100644
index 00000000..92e3c3cf
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[true/1] ids;
+}
+// ----
+// TypeError: (22-28): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol
new file mode 100644
index 00000000..26add45c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[1/true] ids;
+}
+// ----
+// TypeError: (22-28): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol
new file mode 100644
index 00000000..a0d58f4a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[1.111111E1111111111111] ids;
+}
+// ----
+// TypeError: (22-44): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol
new file mode 100644
index 00000000..38a80867
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[3/0] ids;
+}
+// ----
+// TypeError: (22-25): Operator / not compatible with types int_const 3 and int_const 0
diff --git a/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol b/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol
new file mode 100644
index 00000000..7a853a34
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol
@@ -0,0 +1,6 @@
+contract C {
+ bool constant LEN = true;
+ uint[LEN] ids;
+}
+// ----
+// TypeError: (52-55): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol b/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol
new file mode 100644
index 00000000..b44ccfe9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[true] ids;
+}
+// ----
+// TypeError: (22-26): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/parentheses.sol b/test/libsolidity/syntaxTests/arrayLength/parentheses.sol
new file mode 100644
index 00000000..40f55ad6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/parentheses.sol
@@ -0,0 +1,25 @@
+contract C {
+ uint constant L1 = (2);
+ uint constant L2 = ((2));
+ uint constant L3 = ((((2))));
+ uint constant L4 = (2 + 1);
+ uint constant L5 = ((2 + 1));
+ uint constant L6 = (((2) + ((1))));
+ uint constant L7 = (2 + 1) / 1;
+ uint constant L8 = (2 + ((1))) / (1);
+ uint[L1] a1;
+ uint[L2] a2;
+ uint[L3] a3;
+ uint[L4] a4;
+ uint[L5] a5;
+ uint[L6] a6;
+ uint[L7] a7;
+ uint[L8] a8;
+ uint[(2)] a9;
+ uint[(2 + 1)] a10;
+ uint[(2 + 1) + 1] a11;
+ uint[((2) + 1) + 1] a12;
+ uint[(2 + 1) + ((1))] a13;
+ uint[(((2) + 1)) + (((1)))] a14;
+ uint[((((2) + 1)) + (((1))))%1] a15;
+}
diff --git a/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol b/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol
new file mode 100644
index 00000000..b620db76
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint constant LEN = keccak256(ripemd160(33));
+ uint[LEN] ids;
+}
+// ----
+// TypeError: (72-75): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/too_large.sol b/test/libsolidity/syntaxTests/arrayLength/too_large.sol
new file mode 100644
index 00000000..c90a7494
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/too_large.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[8**90] ids;
+}
+// ----
+// TypeError: (22-27): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/arrayLength/tuples.sol b/test/libsolidity/syntaxTests/arrayLength/tuples.sol
new file mode 100644
index 00000000..bc10b3b5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/arrayLength/tuples.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint[(1,2)] a15;
+}
+// ----
+// TypeError: (22-27): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol b/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol
new file mode 100644
index 00000000..26712735
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol
@@ -0,0 +1,7 @@
+contract C {
+ uint constant a = addmod(3, 4, 0.1);
+ uint constant b = mulmod(3, 4, 0.1);
+}
+// ----
+// TypeError: (48-51): Invalid type for argument in function call. Invalid implicit conversion from rational_const 1 / 10 to uint256 requested.
+// TypeError: (89-92): Invalid type for argument in function call. Invalid implicit conversion from rational_const 1 / 10 to uint256 requested.
diff --git a/test/libsolidity/syntaxTests/constants/addmod_zero.sol b/test/libsolidity/syntaxTests/constants/addmod_zero.sol
new file mode 100644
index 00000000..18f7d64a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/addmod_zero.sol
@@ -0,0 +1,11 @@
+contract c {
+ uint constant a1 = 0;
+ uint constant a2 = 1;
+ uint constant b1 = addmod(3, 4, 0);
+ uint constant b2 = addmod(3, 4, a1);
+ uint constant b3 = addmod(3, 4, a2 - 1);
+}
+// ----
+// TypeError: (88-103): Arithmetic modulo zero.
+// TypeError: (128-144): Arithmetic modulo zero.
+// TypeError: (169-189): Arithmetic modulo zero.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol
new file mode 100644
index 00000000..cb553fbe
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant a = a;
+}
+// ----
+// TypeError: (17-36): The value of the constant a has a cyclic dependency via a.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
new file mode 100644
index 00000000..00f9bb0f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint constant a = b * c;
+ uint constant b = 7;
+ uint constant c = b + uint(keccak256(d));
+ uint constant d = 2 + a;
+}
+// ----
+// 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_3.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol
new file mode 100644
index 00000000..969ed50d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol
@@ -0,0 +1,11 @@
+contract C {
+ uint constant x = a;
+ uint constant a = b * c;
+ uint constant b = c;
+ uint constant c = b;
+}
+// ----
+// TypeError: (17-36): The value of the constant x has a cyclic dependency via a.
+// TypeError: (42-65): The value of the constant a has a cyclic dependency via b.
+// TypeError: (71-90): The value of the constant b has a cyclic dependency via c.
+// TypeError: (96-115): The value of the constant c has a cyclic dependency via b.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
new file mode 100644
index 00000000..f01cb98e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint constant a = b * c;
+ uint constant b = 7;
+ uint constant c = 4 + uint(keccak256(d));
+ uint constant d = 2 + b;
+} \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/constants/division_by_zero.sol b/test/libsolidity/syntaxTests/constants/division_by_zero.sol
new file mode 100644
index 00000000..bf6000ec
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/division_by_zero.sol
@@ -0,0 +1,9 @@
+contract c {
+ uint constant a1 = 0;
+ uint constant a2 = 1;
+ uint constant b1 = 7 / a1;
+ uint constant b2 = 7 / (a2 - 1);
+}
+// ----
+// TypeError: (88-94): Division by zero.
+// TypeError: (119-131): Division by zero.
diff --git a/test/libsolidity/syntaxTests/constants/mod_div_rational.sol b/test/libsolidity/syntaxTests/constants/mod_div_rational.sol
new file mode 100644
index 00000000..f8b6ce31
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/mod_div_rational.sol
@@ -0,0 +1,6 @@
+contract C {
+ fixed a1 = 0.1 % -0.4271087646484375;
+ fixed a2 = 0.1 % 0.4271087646484375;
+ fixed a3 = 0 / 0.123;
+ fixed a4 = 0 / -0.123;
+}
diff --git a/test/libsolidity/syntaxTests/constants/mod_zero.sol b/test/libsolidity/syntaxTests/constants/mod_zero.sol
new file mode 100644
index 00000000..f5e4a23a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/mod_zero.sol
@@ -0,0 +1,9 @@
+contract c {
+ uint constant a1 = 0;
+ uint constant a2 = 1;
+ uint constant b1 = 3 % a1;
+ uint constant b2 = 3 % (a2 - 1);
+}
+// ----
+// TypeError: (88-94): Modulo zero.
+// TypeError: (119-131): Modulo zero.
diff --git a/test/libsolidity/syntaxTests/constants/mulmod_zero.sol b/test/libsolidity/syntaxTests/constants/mulmod_zero.sol
new file mode 100644
index 00000000..856d01eb
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/mulmod_zero.sol
@@ -0,0 +1,11 @@
+contract c {
+ uint constant a1 = 0;
+ uint constant a2 = 1;
+ uint constant b1 = mulmod(3, 4, 0);
+ uint constant b2 = mulmod(3, 4, a1);
+ uint constant b3 = mulmod(3, 4, a2 - 1);
+}
+// ----
+// TypeError: (88-103): Arithmetic modulo zero.
+// TypeError: (128-144): Arithmetic modulo zero.
+// TypeError: (169-189): Arithmetic modulo zero.
diff --git a/test/libsolidity/syntaxTests/constants/pure_non_rational.sol b/test/libsolidity/syntaxTests/constants/pure_non_rational.sol
new file mode 100644
index 00000000..4b96f1c7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/pure_non_rational.sol
@@ -0,0 +1,11 @@
+// Tests that the ConstantEvaluator does not crash for pure non-rational functions.
+// Currently it does not evaluate such functions, but this may change in the future
+// causing a division by zero error for a.
+contract C {
+ uint constant a = 1 / (uint(keccak256([0])[0]) - uint(keccak256([0])[0]));
+ uint constant b = 1 / uint(keccak256([0]));
+ uint constant c = uint(keccak256([0]));
+ uint[c] mem;
+}
+// ----
+// TypeError: (392-393): Invalid array length, expected integer literal or constant expression.
diff --git a/test/libsolidity/syntaxTests/constructor_this.sol b/test/libsolidity/syntaxTests/constructor_this.sol
new file mode 100644
index 00000000..9d22a161
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constructor_this.sol
@@ -0,0 +1,12 @@
+contract C {
+ function f() public pure {}
+ constructor() public {
+ C c = this;
+ c.f(); // this does not warn now, but should warn in the future
+ this.f();
+ (this).f();
+ }
+}
+// ----
+// Warning: (172-176): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed.
+// Warning: (191-195): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed.
diff --git a/test/libsolidity/syntaxTests/docstring_empty_description.sol b/test/libsolidity/syntaxTests/docstring_empty_description.sol
new file mode 100644
index 00000000..0caa1b23
--- /dev/null
+++ b/test/libsolidity/syntaxTests/docstring_empty_description.sol
@@ -0,0 +1,6 @@
+contract C {
+ /// @param id
+ function vote(uint id) public;
+}
+// ----
+// DocstringParsingError: No description given for param id
diff --git a/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol
new file mode 100644
index 00000000..fda4a17a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol
@@ -0,0 +1,6 @@
+contract test {
+ uint256 variable;
+ uint128 variable;
+}
+// ----
+// DeclarationError: (36-52): Identifier already declared.
diff --git a/test/libsolidity/syntaxTests/double_variable_declaration.sol b/test/libsolidity/syntaxTests/double_variable_declaration.sol
new file mode 100644
index 00000000..9ab87959
--- /dev/null
+++ b/test/libsolidity/syntaxTests/double_variable_declaration.sol
@@ -0,0 +1,8 @@
+contract test {
+ function f() pure public {
+ uint256 x;
+ if (true) { uint256 x; }
+ }
+}
+// ----
+// DeclarationError: (71-80): Identifier already declared.
diff --git a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol
new file mode 100644
index 00000000..2f47e6dc
--- /dev/null
+++ b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol
@@ -0,0 +1,11 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ uint256 x;
+ if (true) { uint256 x; }
+ }
+}
+// ----
+// Warning: (101-110): This declaration shadows an existing declaration.
+// Warning: (76-85): Unused local variable.
+// Warning: (101-110): Unused local variable.
diff --git a/test/libsolidity/syntaxTests/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol
new file mode 100644
index 00000000..12655309
--- /dev/null
+++ b/test/libsolidity/syntaxTests/empty_struct.sol
@@ -0,0 +1,5 @@
+contract test {
+ struct A {}
+}
+// ----
+// Warning: (17-28): Defining empty structs is deprecated.
diff --git a/test/libsolidity/syntaxTests/empty_struct_050.sol b/test/libsolidity/syntaxTests/empty_struct_050.sol
new file mode 100644
index 00000000..886f1f83
--- /dev/null
+++ b/test/libsolidity/syntaxTests/empty_struct_050.sol
@@ -0,0 +1,6 @@
+pragma experimental "v0.5.0";
+contract test {
+ struct A {}
+}
+// ----
+// SyntaxError: (47-58): Defining empty structs is disallowed.
diff --git a/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol b/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol
new file mode 100644
index 00000000..94da5881
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol
@@ -0,0 +1,13 @@
+contract C {
+ function f() pure public {
+ abi.encodeWithSelector();
+ abi.encodeWithSignature();
+ abi.encodeWithSelector(uint(2), 2);
+ abi.encodeWithSignature(uint(2), 2);
+ }
+}
+// ----
+// TypeError: (52-76): Need at least 1 arguments for function call, but provided only 0.
+// TypeError: (86-111): Need at least 1 arguments for function call, but provided only 0.
+// TypeError: (144-151): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes4 requested.
+// TypeError: (189-196): Invalid type for argument in function call. Invalid implicit conversion from uint256 to string memory requested.
diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol
new file mode 100644
index 00000000..87c3b05b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol
@@ -0,0 +1,8 @@
+contract C {
+ function (uint) external returns (uint) x;
+ function f() public {
+ x.value(2)();
+ }
+}
+// ----
+// TypeError: (94-101): Member "value" not found or not visible after argument-dependent lookup in function (uint256) external returns (uint256) - did you forget the "payable" modifier?
diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol
new file mode 100644
index 00000000..ca2a0196
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol
@@ -0,0 +1,6 @@
+contract C {
+ function (uint) external payable returns (uint) x;
+ function f() public {
+ x.value(2)(1);
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol b/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol
new file mode 100644
index 00000000..2711dae8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public {
+ delete this.f;
+ }
+}
+// ----
+// TypeError: (54-60): Expression has to be an lvalue.
diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol
new file mode 100644
index 00000000..a6fe6c22
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol
@@ -0,0 +1,17 @@
+contract C {
+ function(uint) external returns (uint) x;
+ function(uint) internal returns (uint) y;
+ function f() public {
+ delete x;
+ var a = y;
+ delete a;
+ delete y;
+ var c = f;
+ delete c;
+ function(uint) internal returns (uint) g;
+ delete g;
+ }
+}
+// ----
+// Warning: (157-162): Use of the "var" keyword is deprecated.
+// Warning: (212-217): Use of the "var" keyword is deprecated.
diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol
new file mode 100644
index 00000000..60da19e4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public {
+ delete f;
+ }
+}
+// ----
+// TypeError: (54-55): Expression has to be an lvalue.
diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol
new file mode 100644
index 00000000..eb4f0693
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol
@@ -0,0 +1,10 @@
+// This is a test that checks that the type of the `bytes` parameter is
+// correctly changed from its own type `bytes calldata` to `bytes memory`
+// when converting to a function type.
+contract C {
+ function f(function(bytes memory) pure external /*g*/) pure public { }
+ function callback(bytes) pure external {}
+ function g() view public {
+ f(this.callback);
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol
new file mode 100644
index 00000000..0a6d1e53
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol
@@ -0,0 +1,11 @@
+contract C {
+ function (uint) external public x;
+
+ function g(uint) public {
+ x = this.g;
+ }
+ function f() public view returns (function(uint) external) {
+ return this.x();
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol
new file mode 100644
index 00000000..8b14d3dc
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol
@@ -0,0 +1,5 @@
+contract C {
+ function() external returns (function () internal) x;
+}
+// ----
+// TypeError: (46-67): Internal type cannot be used for external function type.
diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol
new file mode 100644
index 00000000..3e264c8c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol
@@ -0,0 +1,5 @@
+contract C {
+ function(function () internal) external x;
+}
+// ----
+// TypeError: (26-47): Internal type cannot be used for external function type.
diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol
new file mode 100644
index 00000000..b86425db
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol
@@ -0,0 +1,5 @@
+contract C {
+ function f() public view returns (address) {
+ return address(this.f);
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol
new file mode 100644
index 00000000..f4287223
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public returns (uint) {
+ return uint(this.f);
+ }
+}
+// ----
+// TypeError: (69-81): Explicit type conversion not allowed from "function () external returns (uint256)" to "uint256".
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type.sol b/test/libsolidity/syntaxTests/functionTypes/function_type.sol
new file mode 100644
index 00000000..23d50136
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type.sol
@@ -0,0 +1,6 @@
+contract C {
+ function f() pure public {
+ function(uint) returns (uint) x;
+ x;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol
new file mode 100644
index 00000000..ec23d637
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol
@@ -0,0 +1,11 @@
+contract C {
+ function(uint) external returns (uint)[] public x;
+ function(uint) internal returns (uint)[10] y;
+ function f() view public {
+ function(uint) returns (uint)[10] memory a;
+ function(uint) returns (uint)[10] storage b = y;
+ function(uint) external returns (uint)[] memory c;
+ c = new function(uint) external returns (uint)[](200);
+ a; b;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol
new file mode 100644
index 00000000..95ebc179
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol
@@ -0,0 +1,7 @@
+contract C {
+ // Fool parser into parsing a constructor as a function type.
+ constructor() x;
+}
+// ----
+// Warning: (83-99): Modifiers of functions without implementation are ignored.
+// DeclarationError: (97-98): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol
new file mode 100644
index 00000000..b7763d28
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol
@@ -0,0 +1,8 @@
+contract C {
+ // Fool parser into parsing a constructor as a function type.
+ function f() {
+ constructor() x;
+ }
+}
+// ----
+// ParserError: (118-118): Expected token Semicolon got 'Identifier'
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol
new file mode 100644
index 00000000..4eb53227
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol
@@ -0,0 +1,5 @@
+contract C {
+ function(bytes memory) internal public a;
+}
+// ----
+// TypeError: (17-57): Internal or recursive type is not allowed for public state variables.
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol
new file mode 100644
index 00000000..da66ec8a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol
@@ -0,0 +1,7 @@
+contract C {
+ uint x;
+ function f(function(uint) external returns (uint) g) public returns (function(uint) external returns (uint)) {
+ x = 2;
+ return g;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol
new file mode 100644
index 00000000..9cd313c8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol
@@ -0,0 +1,5 @@
+contract C {
+ function f() public pure returns (function(uint) pure external returns (uint) g) {
+ return g;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol
new file mode 100644
index 00000000..f0240472
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol
@@ -0,0 +1,6 @@
+contract test {
+ function fa(bytes memory) public { }
+ function(bytes memory) external internal a = fa;
+}
+// ----
+// TypeError: (106-108): Type function (bytes memory) is not implicitly convertible to expected type function (bytes memory) external.
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol b/test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol
new file mode 100644
index 00000000..36206d63
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol
@@ -0,0 +1,7 @@
+contract C {
+ // This is an error, you should explicitly use
+ // `external public` to fix it - `internal public` does not exist.
+ function(bytes memory) public a;
+}
+// ----
+// TypeError: (139-170): Invalid visibility, can only be "external" or "internal".
diff --git a/test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol b/test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol
new file mode 100644
index 00000000..91c2420a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol
@@ -0,0 +1,9 @@
+contract C {
+ function(bytes memory) a1;
+ function(bytes memory) internal b1;
+ function(bytes memory) internal internal b2;
+ function(bytes memory) external c1;
+ function(bytes memory) external internal c2;
+ function(bytes memory) external public c3;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol
new file mode 100644
index 00000000..fa92d559
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol
@@ -0,0 +1,8 @@
+// It should not be possible to give internal functions
+// as parameters to external functions.
+contract C {
+ function f(function(uint) internal returns (uint) x) public {
+ }
+}
+// ----
+// TypeError: (124-164): Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol
new file mode 100644
index 00000000..b37fb285
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol
@@ -0,0 +1,6 @@
+library L {
+ function f(function(uint) internal returns (uint) x) public {
+ }
+}
+// ----
+// TypeError: (27-67): Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol
new file mode 100644
index 00000000..7ffa447e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol
@@ -0,0 +1,4 @@
+library L {
+ function f(function(uint) internal returns (uint) /*x*/) pure internal {
+ }
+}
diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol
new file mode 100644
index 00000000..41fcd0a4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol
@@ -0,0 +1,7 @@
+// It should not be possible to return internal functions from external functions.
+contract C {
+ function f() public returns (function(uint) internal returns (uint) x) {
+ }
+}
+// ----
+// TypeError: (129-169): Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol
new file mode 100644
index 00000000..b75a0d43
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public returns (address) {
+ return address(f);
+ }
+}
+// ----
+// TypeError: (72-82): Explicit type conversion not allowed from "function () returns (address)" to "address".
diff --git a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol
new file mode 100644
index 00000000..a7cb9d92
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol
@@ -0,0 +1,5 @@
+contract C {
+ function (uint) internal payable returns (uint) x;
+}
+// ----
+// TypeError: (17-66): Only external function types can be payable.
diff --git a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol
new file mode 100644
index 00000000..5c6dc056
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol
@@ -0,0 +1,9 @@
+contract C {
+ function (uint) internal payable returns (uint) x;
+
+ function g() public {
+ x = g;
+ }
+}
+// ----
+// TypeError: (17-66): Only external function types can be payable.
diff --git a/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol
new file mode 100644
index 00000000..9d4f0a09
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public {
+ function(uint) private returns (uint) x;
+ }
+}
+// ----
+// TypeError: (47-86): Invalid visibility, can only be "external" or "internal".
diff --git a/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol
new file mode 100644
index 00000000..756766d3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol
@@ -0,0 +1,7 @@
+contract C {
+ function f() public {
+ function(uint) public returns (uint) x;
+ }
+}
+// ----
+// TypeError: (47-85): Invalid visibility, can only be "external" or "internal".
diff --git a/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol
new file mode 100644
index 00000000..10c6767c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol
@@ -0,0 +1,23 @@
+contract test {
+ function fa(uint) {}
+ function fb(uint) internal {}
+ function fc(uint) internal {}
+ function fd(uint) external {}
+ function fe(uint) external {}
+ function ff(uint) internal {}
+ function fg(uint) internal pure {}
+ function fh(uint) pure internal {}
+
+ function(uint) a = fa;
+ function(uint) internal b = fb; // (explicit internal applies to the function type)
+ function(uint) internal internal c = fc;
+ function(uint) external d = this.fd;
+ function(uint) external internal e = this.fe;
+ function(uint) internal public f = ff;
+ function(uint) internal pure public g = fg;
+ function(uint) pure internal public h = fh;
+}
+// ----
+// TypeError: (545-582): Internal or recursive type is not allowed for public state variables.
+// TypeError: (588-630): Internal or recursive type is not allowed for public state variables.
+// TypeError: (636-678): Internal or recursive type is not allowed for public state variables.
diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol
new file mode 100644
index 00000000..072c7eb7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol
@@ -0,0 +1,5 @@
+contract C {
+ function(uint a) f;
+}
+// ----
+// Warning: (26-32): Naming function type parameters is deprecated.
diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol
new file mode 100644
index 00000000..67a74e54
--- /dev/null
+++ b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol
@@ -0,0 +1,5 @@
+contract C {
+ function(uint) returns (bool ret) f;
+}
+// ----
+// Warning: (41-49): Naming function type return parameters is deprecated.
diff --git a/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol
new file mode 100644
index 00000000..ce9d5f5f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol
@@ -0,0 +1,2 @@
+contract A { constructor() public { } }
+contract B is A { constructor() A() public { } }
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol
new file mode 100644
index 00000000..0b18b995
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol
@@ -0,0 +1,7 @@
+contract Base {
+ constructor(uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base(), Derived() { }
+// ----
+// Warning: (101-107): Wrong argument count for constructor call: 0 arguments given but expected 1.
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol
new file mode 100644
index 00000000..db04ab8c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol
@@ -0,0 +1,9 @@
+pragma experimental "v0.5.0";
+
+contract Base {
+ constructor(uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base(), Derived() { }
+// ----
+// TypeError: (132-138): Wrong argument count for constructor call: 0 arguments given but expected 1.
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol
new file mode 100644
index 00000000..015b33e5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol
@@ -0,0 +1,9 @@
+contract Base {
+ constructor(uint) public { }
+}
+contract Base1 is Base(3) {}
+contract Derived is Base, Base1 {
+ constructor(uint i) Base(i) public {}
+}
+// ----
+// Warning: (138-145): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol
new file mode 100644
index 00000000..24cca8f0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol
@@ -0,0 +1,5 @@
+contract Base {
+ constructor(uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base, Derived {}
diff --git a/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol
new file mode 100644
index 00000000..6cf68d2a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol
@@ -0,0 +1,4 @@
+contract A { constructor() public { } }
+contract B is A { constructor() A public { } }
+// ----
+// Warning: (72-73): Modifier-style base constructor call without arguments.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol
new file mode 100644
index 00000000..24cff54d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol
@@ -0,0 +1,5 @@
+contract A { constructor(uint) public { } }
+contract B is A(2) { constructor() public { } }
+contract C is B { constructor() A(3) public { } }
+// ----
+// Warning: (125-129): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol
new file mode 100644
index 00000000..8d5df5bf
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol
@@ -0,0 +1,7 @@
+pragma experimental "v0.5.0";
+
+contract A { constructor(uint) public { } }
+contract B is A(2) { constructor() public { } }
+contract C is B { constructor() A(3) public { } }
+// ----
+// DeclarationError: (156-160): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol
new file mode 100644
index 00000000..9ceaea5e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol
@@ -0,0 +1,4 @@
+contract A { constructor(uint) public { } }
+contract B is A(2) { constructor() A(3) public { } }
+// ----
+// Warning: (79-83): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol
new file mode 100644
index 00000000..f9325f99
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol
@@ -0,0 +1,6 @@
+pragma experimental "v0.5.0";
+
+contract A { constructor(uint) public { } }
+contract B is A(2) { constructor() A(3) public { } }
+// ----
+// DeclarationError: (110-114): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol
new file mode 100644
index 00000000..e5c2aa36
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol
@@ -0,0 +1,7 @@
+contract C { constructor(uint) public {} }
+contract A is C(2) {}
+contract B is C(2) {}
+contract D is A, B { constructor() C(3) public {} }
+// ----
+// Warning: (122-126): Base constructor arguments given twice.
+// Warning: (122-126): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol
new file mode 100644
index 00000000..1abf2992
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol
@@ -0,0 +1,6 @@
+contract C { constructor(uint) public {} }
+contract A is C(2) {}
+contract B is C(2) {}
+contract D is A, B {}
+// ----
+// Warning: (87-108): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol
new file mode 100644
index 00000000..e15242db
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol
@@ -0,0 +1,6 @@
+contract C { constructor(uint) public {} }
+contract A is C { constructor() C(2) public {} }
+contract B is C { constructor() C(2) public {} }
+contract D is A, B { }
+// ----
+// Warning: (141-163): Base constructor arguments given twice.
diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol
new file mode 100644
index 00000000..c55c41f2
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol
@@ -0,0 +1,10 @@
+contract Base {
+ constructor(uint, uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base {
+ constructor() Base(2) public { }
+}
+// ----
+// TypeError: (74-81): Wrong argument count for constructor call: 1 arguments given but expected 2.
+// TypeError: (130-137): Wrong argument count for modifier invocation: 1 arguments given but expected 2.
diff --git a/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol b/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol
new file mode 100644
index 00000000..b52b4c51
--- /dev/null
+++ b/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant a = 1 / 0;
+}
+// ----
+// TypeError: (35-40): Operator / not compatible with types int_const 1 and int_const 0
diff --git a/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol
new file mode 100644
index 00000000..8cc3b6f2
--- /dev/null
+++ b/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant a = 1 / ((1+3)-4);
+}
+// ----
+// TypeError: (35-48): Operator / not compatible with types int_const 1 and int_const 0
diff --git a/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol b/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol
new file mode 100644
index 00000000..1bbbc3fc
--- /dev/null
+++ b/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant b3 = 1 % 0;
+}
+// ----
+// TypeError: (36-41): Operator % not compatible with types int_const 1 and int_const 0
diff --git a/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol
new file mode 100644
index 00000000..4899cac3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant b3 = 1 % (-4+((2)*2));
+}
+// ----
+// TypeError: (36-52): Operator % not compatible with types int_const 1 and int_const 0
diff --git a/test/libsolidity/syntaxTests/literal_comparisons.sol b/test/libsolidity/syntaxTests/literal_comparisons.sol
new file mode 100644
index 00000000..dd2afcaa
--- /dev/null
+++ b/test/libsolidity/syntaxTests/literal_comparisons.sol
@@ -0,0 +1,7 @@
+contract test {
+ function f(int8 x) public pure {
+ if (x == 1) {}
+ if (1 == x) {}
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol
new file mode 100644
index 00000000..bdbab5d8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol
@@ -0,0 +1,7 @@
+contract C { constructor(uint a) public {} }
+contract B is C {
+ constructor() C(2) C(2) public {}
+}
+// ----
+// Warning: (81-85): Base constructor arguments given twice.
+// DeclarationError: (86-90): Base constructor already provided.
diff --git a/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol b/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol
new file mode 100644
index 00000000..4a2b5c4a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol
@@ -0,0 +1,9 @@
+// This caused a segfault in an earlier version
+contract C {
+ constructor() public {}
+}
+contract D is C {
+ constructor() C(5) public {}
+}
+// ----
+// TypeError: (127-131): Wrong argument count for modifier invocation: 1 arguments given but expected 0.
diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol
new file mode 100644
index 00000000..75624192
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol
@@ -0,0 +1,4 @@
+contract B {
+ function f(uint x) mod(x) mod(2) public pure { }
+ modifier mod(uint a) { if (a > 0) _; }
+}
diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol
new file mode 100644
index 00000000..e15fcf49
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol
@@ -0,0 +1,5 @@
+contract B {
+ function f() mod1(2, true) mod2("0123456") pure public { }
+ modifier mod1(uint a, bool b) { if (b) _; }
+ modifier mod2(bytes7 a) { while (a == "1234567") _; }
+}
diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol
new file mode 100644
index 00000000..00031924
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol
@@ -0,0 +1,4 @@
+contract B {
+ function f() mod(x) pure public { uint x = 7; }
+ modifier mod(uint a) { if (a > 0) _; }
+}
diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol
new file mode 100644
index 00000000..c19ccf2c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol
@@ -0,0 +1,7 @@
+pragma experimental "v0.5.0";
+contract B {
+ function f() mod(x) pure public { uint x = 7; }
+ modifier mod(uint a) { if (a > 0) _; }
+}
+// ----
+// DeclarationError: (64-65): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol
new file mode 100644
index 00000000..de2a8f48
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol
@@ -0,0 +1,5 @@
+contract B {
+ function f(uint8 a) mod1(a, true) mod2(r) pure public returns (bytes7 r) { }
+ modifier mod1(uint a, bool b) { if (b) _; }
+ modifier mod2(bytes7 a) { while (a == "1234567") _; }
+}
diff --git a/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol b/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol
new file mode 100644
index 00000000..a64c2790
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol
@@ -0,0 +1,5 @@
+contract A { function mod(uint a) public { } }
+contract B is A { modifier mod(uint a) { _; } }
+// ----
+// DeclarationError: (65-92): Identifier already declared.
+// TypeError: (65-92): Override changes function to modifier.
diff --git a/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol b/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol
new file mode 100644
index 00000000..958be686
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol
@@ -0,0 +1,4 @@
+contract A { modifier mod(uint a) { _; } }
+contract B is A { modifier mod(uint8 a) { _; } }
+// ----
+// TypeError: (61-89): Override changes modifier signature.
diff --git a/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol b/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol
new file mode 100644
index 00000000..c1e3108b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol
@@ -0,0 +1,6 @@
+contract B {
+ function f() mod1(true) public { }
+ modifier mod1(uint a) { if (a > 0) _; }
+}
+// ----
+// TypeError: (35-39): Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested.
diff --git a/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol b/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol
new file mode 100644
index 00000000..51c3fd80
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol
@@ -0,0 +1,2 @@
+contract A { modifier mod(uint a) { _; } }
+contract B is A { modifier mod(uint a) { _; } }
diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol
new file mode 100644
index 00000000..a43646c3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol
@@ -0,0 +1,5 @@
+contract A { modifier mod(uint a) { _; } }
+contract B is A { function mod(uint a) public { } }
+// ----
+// DeclarationError: (61-92): Identifier already declared.
+// TypeError: (13-40): Override changes modifier to function.
diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol b/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol
new file mode 100644
index 00000000..d22e836c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol
@@ -0,0 +1,6 @@
+contract A {
+ function f(uint a) mod(2) public returns (uint r) { }
+ modifier mod(uint a) { _; return 7; }
+}
+// ----
+// TypeError: (101-109): Return arguments not allowed.
diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol b/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol
new file mode 100644
index 00000000..6198d3c5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol
@@ -0,0 +1,5 @@
+contract test {
+ modifier m() {}
+}
+// ----
+// SyntaxError: (33-35): Modifier body does not contain '_'.
diff --git a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol
new file mode 100644
index 00000000..af1babbc
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol
@@ -0,0 +1,10 @@
+pragma experimental "v0.5.0";
+contract C
+{
+ modifier only_owner() { _; }
+ function foo() only_owner public;
+ function bar() public only_owner;
+}
+// ----
+// SyntaxError: (80-113): Functions without implementation cannot have modifiers.
+// SyntaxError: (118-151): Functions without implementation cannot have modifiers.
diff --git a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol
new file mode 100644
index 00000000..e18c5cf9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol
@@ -0,0 +1,13 @@
+// Previous versions of Solidity turned this
+// into a parser error (they wrongly recognized
+// these functions as state variables of
+// function type).
+contract C
+{
+ modifier only_owner() { _; }
+ function foo() only_owner public;
+ function bar() public only_owner;
+}
+// ----
+// Warning: (203-236): Modifiers of functions without implementation are ignored.
+// Warning: (241-274): Modifiers of functions without implementation are ignored.
diff --git a/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol
new file mode 100644
index 00000000..2d75f29b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol
@@ -0,0 +1,524 @@
+contract C {
+ function f() {
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ b = 5;
+ }
+}
+// ----
+// DeclarationError: (34-35): Undeclared identifier.
+// DeclarationError: (45-46): Undeclared identifier.
+// DeclarationError: (56-57): Undeclared identifier.
+// DeclarationError: (67-68): Undeclared identifier.
+// DeclarationError: (78-79): Undeclared identifier.
+// DeclarationError: (89-90): Undeclared identifier.
+// DeclarationError: (100-101): Undeclared identifier.
+// DeclarationError: (111-112): Undeclared identifier.
+// DeclarationError: (122-123): Undeclared identifier.
+// DeclarationError: (133-134): Undeclared identifier.
+// DeclarationError: (144-145): Undeclared identifier.
+// DeclarationError: (155-156): Undeclared identifier.
+// DeclarationError: (166-167): Undeclared identifier.
+// DeclarationError: (177-178): Undeclared identifier.
+// DeclarationError: (188-189): Undeclared identifier.
+// DeclarationError: (199-200): Undeclared identifier.
+// DeclarationError: (210-211): Undeclared identifier.
+// DeclarationError: (221-222): Undeclared identifier.
+// DeclarationError: (232-233): Undeclared identifier.
+// DeclarationError: (243-244): Undeclared identifier.
+// DeclarationError: (254-255): Undeclared identifier.
+// DeclarationError: (265-266): Undeclared identifier.
+// DeclarationError: (276-277): Undeclared identifier.
+// DeclarationError: (287-288): Undeclared identifier.
+// DeclarationError: (298-299): Undeclared identifier.
+// DeclarationError: (309-310): Undeclared identifier.
+// DeclarationError: (320-321): Undeclared identifier.
+// DeclarationError: (331-332): Undeclared identifier.
+// DeclarationError: (342-343): Undeclared identifier.
+// DeclarationError: (353-354): Undeclared identifier.
+// DeclarationError: (364-365): Undeclared identifier.
+// DeclarationError: (375-376): Undeclared identifier.
+// DeclarationError: (386-387): Undeclared identifier.
+// DeclarationError: (397-398): Undeclared identifier.
+// DeclarationError: (408-409): Undeclared identifier.
+// DeclarationError: (419-420): Undeclared identifier.
+// DeclarationError: (430-431): Undeclared identifier.
+// DeclarationError: (441-442): Undeclared identifier.
+// DeclarationError: (452-453): Undeclared identifier.
+// DeclarationError: (463-464): Undeclared identifier.
+// DeclarationError: (474-475): Undeclared identifier.
+// DeclarationError: (485-486): Undeclared identifier.
+// DeclarationError: (496-497): Undeclared identifier.
+// DeclarationError: (507-508): Undeclared identifier.
+// DeclarationError: (518-519): Undeclared identifier.
+// DeclarationError: (529-530): Undeclared identifier.
+// DeclarationError: (540-541): Undeclared identifier.
+// DeclarationError: (551-552): Undeclared identifier.
+// DeclarationError: (562-563): Undeclared identifier.
+// DeclarationError: (573-574): Undeclared identifier.
+// DeclarationError: (584-585): Undeclared identifier.
+// DeclarationError: (595-596): Undeclared identifier.
+// DeclarationError: (606-607): Undeclared identifier.
+// DeclarationError: (617-618): Undeclared identifier.
+// DeclarationError: (628-629): Undeclared identifier.
+// DeclarationError: (639-640): Undeclared identifier.
+// DeclarationError: (650-651): Undeclared identifier.
+// DeclarationError: (661-662): Undeclared identifier.
+// DeclarationError: (672-673): Undeclared identifier.
+// DeclarationError: (683-684): Undeclared identifier.
+// DeclarationError: (694-695): Undeclared identifier.
+// DeclarationError: (705-706): Undeclared identifier.
+// DeclarationError: (716-717): Undeclared identifier.
+// DeclarationError: (727-728): Undeclared identifier.
+// DeclarationError: (738-739): Undeclared identifier.
+// DeclarationError: (749-750): Undeclared identifier.
+// DeclarationError: (760-761): Undeclared identifier.
+// DeclarationError: (771-772): Undeclared identifier.
+// DeclarationError: (782-783): Undeclared identifier.
+// DeclarationError: (793-794): Undeclared identifier.
+// DeclarationError: (804-805): Undeclared identifier.
+// DeclarationError: (815-816): Undeclared identifier.
+// DeclarationError: (826-827): Undeclared identifier.
+// DeclarationError: (837-838): Undeclared identifier.
+// DeclarationError: (848-849): Undeclared identifier.
+// DeclarationError: (859-860): Undeclared identifier.
+// DeclarationError: (870-871): Undeclared identifier.
+// DeclarationError: (881-882): Undeclared identifier.
+// DeclarationError: (892-893): Undeclared identifier.
+// DeclarationError: (903-904): Undeclared identifier.
+// DeclarationError: (914-915): Undeclared identifier.
+// DeclarationError: (925-926): Undeclared identifier.
+// DeclarationError: (936-937): Undeclared identifier.
+// DeclarationError: (947-948): Undeclared identifier.
+// DeclarationError: (958-959): Undeclared identifier.
+// DeclarationError: (969-970): Undeclared identifier.
+// DeclarationError: (980-981): Undeclared identifier.
+// DeclarationError: (991-992): Undeclared identifier.
+// DeclarationError: (1002-1003): Undeclared identifier.
+// DeclarationError: (1013-1014): Undeclared identifier.
+// DeclarationError: (1024-1025): Undeclared identifier.
+// DeclarationError: (1035-1036): Undeclared identifier.
+// DeclarationError: (1046-1047): Undeclared identifier.
+// DeclarationError: (1057-1058): Undeclared identifier.
+// DeclarationError: (1068-1069): Undeclared identifier.
+// DeclarationError: (1079-1080): Undeclared identifier.
+// DeclarationError: (1090-1091): Undeclared identifier.
+// DeclarationError: (1101-1102): Undeclared identifier.
+// DeclarationError: (1112-1113): Undeclared identifier.
+// DeclarationError: (1123-1124): Undeclared identifier.
+// DeclarationError: (1134-1135): Undeclared identifier.
+// DeclarationError: (1145-1146): Undeclared identifier.
+// DeclarationError: (1156-1157): Undeclared identifier.
+// DeclarationError: (1167-1168): Undeclared identifier.
+// DeclarationError: (1178-1179): Undeclared identifier.
+// DeclarationError: (1189-1190): Undeclared identifier.
+// DeclarationError: (1200-1201): Undeclared identifier.
+// DeclarationError: (1211-1212): Undeclared identifier.
+// DeclarationError: (1222-1223): Undeclared identifier.
+// DeclarationError: (1233-1234): Undeclared identifier.
+// DeclarationError: (1244-1245): Undeclared identifier.
+// DeclarationError: (1255-1256): Undeclared identifier.
+// DeclarationError: (1266-1267): Undeclared identifier.
+// DeclarationError: (1277-1278): Undeclared identifier.
+// DeclarationError: (1288-1289): Undeclared identifier.
+// DeclarationError: (1299-1300): Undeclared identifier.
+// DeclarationError: (1310-1311): Undeclared identifier.
+// DeclarationError: (1321-1322): Undeclared identifier.
+// DeclarationError: (1332-1333): Undeclared identifier.
+// DeclarationError: (1343-1344): Undeclared identifier.
+// DeclarationError: (1354-1355): Undeclared identifier.
+// DeclarationError: (1365-1366): Undeclared identifier.
+// DeclarationError: (1376-1377): Undeclared identifier.
+// DeclarationError: (1387-1388): Undeclared identifier.
+// DeclarationError: (1398-1399): Undeclared identifier.
+// DeclarationError: (1409-1410): Undeclared identifier.
+// DeclarationError: (1420-1421): Undeclared identifier.
+// DeclarationError: (1431-1432): Undeclared identifier.
+// DeclarationError: (1442-1443): Undeclared identifier.
+// DeclarationError: (1453-1454): Undeclared identifier.
+// DeclarationError: (1464-1465): Undeclared identifier.
+// DeclarationError: (1475-1476): Undeclared identifier.
+// DeclarationError: (1486-1487): Undeclared identifier.
+// DeclarationError: (1497-1498): Undeclared identifier.
+// DeclarationError: (1508-1509): Undeclared identifier.
+// DeclarationError: (1519-1520): Undeclared identifier.
+// DeclarationError: (1530-1531): Undeclared identifier.
+// DeclarationError: (1541-1542): Undeclared identifier.
+// DeclarationError: (1552-1553): Undeclared identifier.
+// DeclarationError: (1563-1564): Undeclared identifier.
+// DeclarationError: (1574-1575): Undeclared identifier.
+// DeclarationError: (1585-1586): Undeclared identifier.
+// DeclarationError: (1596-1597): Undeclared identifier.
+// DeclarationError: (1607-1608): Undeclared identifier.
+// DeclarationError: (1618-1619): Undeclared identifier.
+// DeclarationError: (1629-1630): Undeclared identifier.
+// DeclarationError: (1640-1641): Undeclared identifier.
+// DeclarationError: (1651-1652): Undeclared identifier.
+// DeclarationError: (1662-1663): Undeclared identifier.
+// DeclarationError: (1673-1674): Undeclared identifier.
+// DeclarationError: (1684-1685): Undeclared identifier.
+// DeclarationError: (1695-1696): Undeclared identifier.
+// DeclarationError: (1706-1707): Undeclared identifier.
+// DeclarationError: (1717-1718): Undeclared identifier.
+// DeclarationError: (1728-1729): Undeclared identifier.
+// DeclarationError: (1739-1740): Undeclared identifier.
+// DeclarationError: (1750-1751): Undeclared identifier.
+// DeclarationError: (1761-1762): Undeclared identifier.
+// DeclarationError: (1772-1773): Undeclared identifier.
+// DeclarationError: (1783-1784): Undeclared identifier.
+// DeclarationError: (1794-1795): Undeclared identifier.
+// DeclarationError: (1805-1806): Undeclared identifier.
+// DeclarationError: (1816-1817): Undeclared identifier.
+// DeclarationError: (1827-1828): Undeclared identifier.
+// DeclarationError: (1838-1839): Undeclared identifier.
+// DeclarationError: (1849-1850): Undeclared identifier.
+// DeclarationError: (1860-1861): Undeclared identifier.
+// DeclarationError: (1871-1872): Undeclared identifier.
+// DeclarationError: (1882-1883): Undeclared identifier.
+// DeclarationError: (1893-1894): Undeclared identifier.
+// DeclarationError: (1904-1905): Undeclared identifier.
+// DeclarationError: (1915-1916): Undeclared identifier.
+// DeclarationError: (1926-1927): Undeclared identifier.
+// DeclarationError: (1937-1938): Undeclared identifier.
+// DeclarationError: (1948-1949): Undeclared identifier.
+// DeclarationError: (1959-1960): Undeclared identifier.
+// DeclarationError: (1970-1971): Undeclared identifier.
+// DeclarationError: (1981-1982): Undeclared identifier.
+// DeclarationError: (1992-1993): Undeclared identifier.
+// DeclarationError: (2003-2004): Undeclared identifier.
+// DeclarationError: (2014-2015): Undeclared identifier.
+// DeclarationError: (2025-2026): Undeclared identifier.
+// DeclarationError: (2036-2037): Undeclared identifier.
+// DeclarationError: (2047-2048): Undeclared identifier.
+// DeclarationError: (2058-2059): Undeclared identifier.
+// DeclarationError: (2069-2070): Undeclared identifier.
+// DeclarationError: (2080-2081): Undeclared identifier.
+// DeclarationError: (2091-2092): Undeclared identifier.
+// DeclarationError: (2102-2103): Undeclared identifier.
+// DeclarationError: (2113-2114): Undeclared identifier.
+// DeclarationError: (2124-2125): Undeclared identifier.
+// DeclarationError: (2135-2136): Undeclared identifier.
+// DeclarationError: (2146-2147): Undeclared identifier.
+// DeclarationError: (2157-2158): Undeclared identifier.
+// DeclarationError: (2168-2169): Undeclared identifier.
+// DeclarationError: (2179-2180): Undeclared identifier.
+// DeclarationError: (2190-2191): Undeclared identifier.
+// DeclarationError: (2201-2202): Undeclared identifier.
+// DeclarationError: (2212-2213): Undeclared identifier.
+// DeclarationError: (2223-2224): Undeclared identifier.
+// DeclarationError: (2234-2235): Undeclared identifier.
+// DeclarationError: (2245-2246): Undeclared identifier.
+// DeclarationError: (2256-2257): Undeclared identifier.
+// DeclarationError: (2267-2268): Undeclared identifier.
+// DeclarationError: (2278-2279): Undeclared identifier.
+// DeclarationError: (2289-2290): Undeclared identifier.
+// DeclarationError: (2300-2301): Undeclared identifier.
+// DeclarationError: (2311-2312): Undeclared identifier.
+// DeclarationError: (2322-2323): Undeclared identifier.
+// DeclarationError: (2333-2334): Undeclared identifier.
+// DeclarationError: (2344-2345): Undeclared identifier.
+// DeclarationError: (2355-2356): Undeclared identifier.
+// DeclarationError: (2366-2367): Undeclared identifier.
+// DeclarationError: (2377-2378): Undeclared identifier.
+// DeclarationError: (2388-2389): Undeclared identifier.
+// DeclarationError: (2399-2400): Undeclared identifier.
+// DeclarationError: (2410-2411): Undeclared identifier.
+// DeclarationError: (2421-2422): Undeclared identifier.
+// DeclarationError: (2432-2433): Undeclared identifier.
+// DeclarationError: (2443-2444): Undeclared identifier.
+// DeclarationError: (2454-2455): Undeclared identifier.
+// DeclarationError: (2465-2466): Undeclared identifier.
+// DeclarationError: (2476-2477): Undeclared identifier.
+// DeclarationError: (2487-2488): Undeclared identifier.
+// DeclarationError: (2498-2499): Undeclared identifier.
+// DeclarationError: (2509-2510): Undeclared identifier.
+// DeclarationError: (2520-2521): Undeclared identifier.
+// DeclarationError: (2531-2532): Undeclared identifier.
+// DeclarationError: (2542-2543): Undeclared identifier.
+// DeclarationError: (2553-2554): Undeclared identifier.
+// DeclarationError: (2564-2565): Undeclared identifier.
+// DeclarationError: (2575-2576): Undeclared identifier.
+// DeclarationError: (2586-2587): Undeclared identifier.
+// DeclarationError: (2597-2598): Undeclared identifier.
+// DeclarationError: (2608-2609): Undeclared identifier.
+// DeclarationError: (2619-2620): Undeclared identifier.
+// DeclarationError: (2630-2631): Undeclared identifier.
+// DeclarationError: (2641-2642): Undeclared identifier.
+// DeclarationError: (2652-2653): Undeclared identifier.
+// DeclarationError: (2663-2664): Undeclared identifier.
+// DeclarationError: (2674-2675): Undeclared identifier.
+// DeclarationError: (2685-2686): Undeclared identifier.
+// DeclarationError: (2696-2697): Undeclared identifier.
+// DeclarationError: (2707-2708): Undeclared identifier.
+// DeclarationError: (2718-2719): Undeclared identifier.
+// DeclarationError: (2729-2730): Undeclared identifier.
+// DeclarationError: (2740-2741): Undeclared identifier.
+// DeclarationError: (2751-2752): Undeclared identifier.
+// DeclarationError: (2762-2763): Undeclared identifier.
+// DeclarationError: (2773-2774): Undeclared identifier.
+// DeclarationError: (2784-2785): Undeclared identifier.
+// DeclarationError: (2795-2796): Undeclared identifier.
+// DeclarationError: (2806-2807): Undeclared identifier.
+// DeclarationError: (2817-2818): Undeclared identifier.
+// DeclarationError: (2828-2829): Undeclared identifier.
+// DeclarationError: (2839-2840): Undeclared identifier.
+// Warning: There are more than 256 errors. Aborting.
diff --git a/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol b/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol
new file mode 100644
index 00000000..2c9b8a42
--- /dev/null
+++ b/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol
@@ -0,0 +1,524 @@
+contract C {
+ function f() {
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ continue;
+ }
+}
+// ----
+// SyntaxError: (34-42): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (48-56): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (62-70): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (76-84): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (90-98): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (104-112): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (118-126): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (132-140): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (146-154): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (160-168): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (174-182): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (188-196): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (202-210): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (216-224): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (230-238): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (244-252): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (258-266): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (272-280): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (286-294): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (300-308): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (314-322): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (328-336): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (342-350): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (356-364): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (370-378): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (384-392): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (398-406): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (412-420): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (426-434): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (440-448): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (454-462): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (468-476): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (482-490): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (496-504): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (510-518): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (524-532): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (538-546): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (552-560): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (566-574): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (580-588): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (594-602): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (608-616): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (622-630): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (636-644): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (650-658): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (664-672): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (678-686): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (692-700): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (706-714): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (720-728): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (734-742): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (748-756): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (762-770): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (776-784): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (790-798): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (804-812): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (818-826): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (832-840): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (846-854): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (860-868): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (874-882): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (888-896): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (902-910): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (916-924): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (930-938): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (944-952): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (958-966): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (972-980): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (986-994): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1000-1008): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1014-1022): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1028-1036): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1042-1050): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1056-1064): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1070-1078): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1084-1092): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1098-1106): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1112-1120): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1126-1134): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1140-1148): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1154-1162): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1168-1176): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1182-1190): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1196-1204): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1210-1218): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1224-1232): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1238-1246): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1252-1260): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1266-1274): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1280-1288): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1294-1302): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1308-1316): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1322-1330): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1336-1344): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1350-1358): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1364-1372): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1378-1386): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1392-1400): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1406-1414): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1420-1428): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1434-1442): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1448-1456): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1462-1470): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1476-1484): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1490-1498): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1504-1512): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1518-1526): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1532-1540): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1546-1554): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1560-1568): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1574-1582): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1588-1596): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1602-1610): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1616-1624): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1630-1638): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1644-1652): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1658-1666): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1672-1680): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1686-1694): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1700-1708): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1714-1722): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1728-1736): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1742-1750): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1756-1764): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1770-1778): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1784-1792): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1798-1806): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1812-1820): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1826-1834): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1840-1848): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1854-1862): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1868-1876): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1882-1890): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1896-1904): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1910-1918): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1924-1932): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1938-1946): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1952-1960): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1966-1974): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1980-1988): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (1994-2002): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2008-2016): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2022-2030): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2036-2044): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2050-2058): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2064-2072): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2078-2086): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2092-2100): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2106-2114): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2120-2128): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2134-2142): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2148-2156): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2162-2170): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2176-2184): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2190-2198): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2204-2212): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2218-2226): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2232-2240): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2246-2254): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2260-2268): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2274-2282): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2288-2296): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2302-2310): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2316-2324): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2330-2338): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2344-2352): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2358-2366): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2372-2380): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2386-2394): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2400-2408): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2414-2422): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2428-2436): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2442-2450): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2456-2464): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2470-2478): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2484-2492): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2498-2506): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2512-2520): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2526-2534): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2540-2548): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2554-2562): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2568-2576): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2582-2590): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2596-2604): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2610-2618): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2624-2632): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2638-2646): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2652-2660): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2666-2674): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2680-2688): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2694-2702): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2708-2716): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2722-2730): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2736-2744): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2750-2758): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2764-2772): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2778-2786): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2792-2800): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2806-2814): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2820-2828): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2834-2842): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2848-2856): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2862-2870): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2876-2884): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2890-2898): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2904-2912): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2918-2926): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2932-2940): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2946-2954): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2960-2968): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2974-2982): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (2988-2996): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3002-3010): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3016-3024): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3030-3038): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3044-3052): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3058-3066): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3072-3080): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3086-3094): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3100-3108): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3114-3122): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3128-3136): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3142-3150): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3156-3164): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3170-3178): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3184-3192): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3198-3206): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3212-3220): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3226-3234): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3240-3248): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3254-3262): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3268-3276): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3282-3290): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3296-3304): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3310-3318): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3324-3332): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3338-3346): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3352-3360): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3366-3374): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3380-3388): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3394-3402): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3408-3416): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3422-3430): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3436-3444): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3450-3458): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3464-3472): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3478-3486): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3492-3500): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3506-3514): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3520-3528): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3534-3542): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3548-3556): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3562-3570): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3576-3584): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3590-3598): "continue" has to be in a "for" or "while" loop.
+// SyntaxError: (3604-3612): "continue" has to be in a "for" or "while" loop.
+// Warning: There are more than 256 errors. Aborting.
diff --git a/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol b/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol
new file mode 100644
index 00000000..9f714aea
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol
@@ -0,0 +1,28 @@
+contract A {
+ function a() public pure {
+ }
+}
+contract B {
+ constructor(address) public {
+ }
+ function b(address) public returns (A) {
+ return new A();
+ }
+}
+contract C {
+ B m_b;
+ C m_c;
+ constructor(C other_c) public {
+ m_c = other_c;
+ m_b = new B(this);
+ m_b.b(this).a();
+ g(this).f();
+ other_c.f();
+ m_c.f();
+ }
+ function f() public pure {
+ }
+ function g(C) public view returns (C) {
+ return m_c;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/constructor_super.sol b/test/libsolidity/syntaxTests/parsing/constructor_super.sol
new file mode 100644
index 00000000..fa1be187
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/constructor_super.sol
@@ -0,0 +1,10 @@
+contract A {
+ function x() pure internal {}
+}
+
+contract B is A {
+ constructor() public {
+ // used to trigger warning about using ``this`` in constructor
+ super.x();
+ }
+}
diff --git a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
new file mode 100644
index 00000000..fd3067e3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
@@ -0,0 +1,5 @@
+contract test {
+ uint256 ;
+}
+// ----
+// ParserError: (28-28): Expected identifier, got 'Semicolon'
diff --git a/test/libsolidity/syntaxTests/parsing/return_var.sol b/test/libsolidity/syntaxTests/parsing/return_var.sol
new file mode 100644
index 00000000..47ac9ef0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/return_var.sol
@@ -0,0 +1,25 @@
+contract C {
+ function f() returns(var) {}
+ function f() returns(var x) {}
+ function f() returns(var x, uint y) {}
+ function f() returns(uint x, var y) {}
+ function f() returns(var x, var y) {}
+ function f() public pure returns (var storage) {}
+ function f() public pure returns (var storage x) {}
+ 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.
diff --git a/test/libsolidity/syntaxTests/parsing/smoke_test.sol b/test/libsolidity/syntaxTests/parsing/smoke_test.sol
new file mode 100644
index 00000000..d328b167
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/smoke_test.sol
@@ -0,0 +1,4 @@
+contract test {
+ uint256 stateVariable1;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol
new file mode 100644
index 00000000..e041247d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol
@@ -0,0 +1,25 @@
+contract C {
+ function f(var) public pure {}
+ function f(var x) public pure {}
+ function f(var x, var y) public pure {}
+ function f(uint x, var y) public pure {}
+ function f(var x, uint y) public pure {}
+ function f(var storage) public pure {}
+ function f(var storage x) public pure {}
+ 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.
diff --git a/test/libsolidity/syntaxTests/parsing/var_storage_var.sol b/test/libsolidity/syntaxTests/parsing/var_storage_var.sol
new file mode 100644
index 00000000..431d4ca5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/var_storage_var.sol
@@ -0,0 +1,5 @@
+contract C {
+ var a;
+}
+// ----
+// ParserError: (17-17): Function, variable, struct or modifier declaration expected.
diff --git a/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol
new file mode 100644
index 00000000..dfd67aaa
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol
@@ -0,0 +1,6 @@
+contract test {
+ function fun() public { }
+ function fun() public { }
+}
+// ----
+// DeclarationError: (20-45): Function with same name and arguments defined twice.
diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol
new file mode 100644
index 00000000..d90ec2d7
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol
@@ -0,0 +1,8 @@
+contract test {
+ function f() pure public {
+ { uint x; }
+ { uint x; }
+ }
+}
+// ----
+// DeclarationError: (77-83): Identifier already declared.
diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol
new file mode 100644
index 00000000..06bfe7be
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol
@@ -0,0 +1,10 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ { uint x; }
+ { uint x; }
+ }
+}
+// ----
+// Warning: (87-93): Unused local variable.
+// Warning: (107-113): Unused local variable.
diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol
new file mode 100644
index 00000000..1a5ff2f9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol
@@ -0,0 +1,8 @@
+contract test {
+ function f() pure public {
+ { uint x; }
+ uint x;
+ }
+}
+// ----
+// DeclarationError: (75-81): Identifier already declared.
diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol
new file mode 100644
index 00000000..20ea0349
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol
@@ -0,0 +1,10 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ { uint x; }
+ uint x;
+ }
+}
+// ----
+// Warning: (87-93): Unused local variable.
+// Warning: (105-111): Unused local variable.
diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol
new file mode 100644
index 00000000..67ada4a4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol
@@ -0,0 +1,6 @@
+contract test {
+ uint256 variable;
+ function f() pure public { uint32 variable; variable = 2; }
+}
+// ----
+// Warning: (69-84): This declaration shadows an existing declaration.
diff --git a/test/libsolidity/syntaxTests/scoping/scoping.sol b/test/libsolidity/syntaxTests/scoping/scoping.sol
new file mode 100644
index 00000000..34b055d9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping.sol
@@ -0,0 +1,11 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() public {
+ {
+ uint256 x;
+ }
+ x = 2;
+ }
+}
+// ----
+// DeclarationError: (123-124): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol
new file mode 100644
index 00000000..7334bc49
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol
@@ -0,0 +1,9 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ x = 3;
+ uint x;
+ }
+}
+// ----
+// DeclarationError: (85-86): Undeclared identifier. Did you mean "x"?
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol
new file mode 100644
index 00000000..d893a889
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol
@@ -0,0 +1,6 @@
+contract test {
+ function f() pure public {
+ x = 3;
+ uint x;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for.sol b/test/libsolidity/syntaxTests/scoping/scoping_for.sol
new file mode 100644
index 00000000..6e5b7095
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_for.sol
@@ -0,0 +1,8 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ for (uint x = 0; x < 10; x ++){
+ x = 2;
+ }
+ }
+}
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for2.sol b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol
new file mode 100644
index 00000000..eb74b8ab
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol
@@ -0,0 +1,7 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ for (uint x = 0; x < 10; x ++)
+ x = 2;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol
new file mode 100644
index 00000000..1814cb47
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol
@@ -0,0 +1,11 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ for (uint x = 0; x < 10; x ++){
+ x = 2;
+ }
+ x = 4;
+ }
+}
+// ----
+// DeclarationError: (154-155): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol
new file mode 100644
index 00000000..3e80b385
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol
@@ -0,0 +1,10 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ for (;; y++){
+ uint y = 3;
+ }
+ }
+}
+// ----
+// DeclarationError: (93-94): Undeclared identifier.
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_old.sol
new file mode 100644
index 00000000..83f6b60b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_old.sol
@@ -0,0 +1,6 @@
+contract test {
+ function f() pure public {
+ x = 4;
+ uint256 x = 2;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol
new file mode 100644
index 00000000..9e2c0171
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol
@@ -0,0 +1,5 @@
+contract test {
+ function f() pure public {
+ uint a = a;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol
new file mode 100644
index 00000000..ab3dcefb
--- /dev/null
+++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol
@@ -0,0 +1,8 @@
+pragma experimental "v0.5.0";
+contract test {
+ function f() pure public {
+ uint a = a;
+ }
+}
+// ----
+// DeclarationError: (94-95): Undeclared identifier. Did you mean "a"?
diff --git a/test/libsolidity/syntaxTests/signed_rational_modulus.sol b/test/libsolidity/syntaxTests/signed_rational_modulus.sol
new file mode 100644
index 00000000..b37d33d0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/signed_rational_modulus.sol
@@ -0,0 +1,8 @@
+contract test {
+ function f() public pure {
+ fixed a = 0.42578125 % -0.4271087646484375;
+ fixed b = .5 % a;
+ fixed c = a % b;
+ a; b; c;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/smoke_test.sol b/test/libsolidity/syntaxTests/smoke_test.sol
new file mode 100644
index 00000000..6abaff18
--- /dev/null
+++ b/test/libsolidity/syntaxTests/smoke_test.sol
@@ -0,0 +1,6 @@
+contract test {
+ uint256 stateVariable1;
+ function fun(uint256 arg1) public { uint256 y; y = arg1; }
+}
+// ----
+// Warning: (42-100): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol
new file mode 100644
index 00000000..d9eebee4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol
@@ -0,0 +1,17 @@
+contract C {
+ struct S { uint x; }
+ S s;
+ struct T { uint y; }
+ T t;
+ function f() public view {
+ abi.encode(s, t);
+ }
+ function g() public view {
+ abi.encodePacked(s, t);
+ }
+}
+// ----
+// TypeError: (131-132): This type cannot be encoded.
+// TypeError: (134-135): This type cannot be encoded.
+// TypeError: (200-201): This type cannot be encoded.
+// TypeError: (203-204): This type cannot be encoded.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol
new file mode 100644
index 00000000..d6cf60e4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol
@@ -0,0 +1,18 @@
+pragma experimental ABIEncoderV2;
+
+contract C {
+ struct S { uint x; }
+ S s;
+ struct T { uint y; }
+ T t;
+ function f() public view {
+ abi.encode(s, t);
+ }
+ function g() public view {
+ abi.encodePacked(s, t);
+ }
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// TypeError: (235-236): This type cannot be encoded.
+// TypeError: (238-239): This type cannot be encoded.
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
new file mode 100644
index 00000000..c98d7a57
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() public pure {
+ bytes32 h = keccak256(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.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol
new file mode 100644
index 00000000..f1b5606e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol
@@ -0,0 +1,13 @@
+contract C {
+ function f() public pure {
+ bool a = address(this).call(address(this).delegatecall, super);
+ bool b = address(this).delegatecall(log0, tx, mulmod);
+ a; b;
+ }
+}
+// ----
+// TypeError: (80-106): This type cannot be encoded.
+// TypeError: (108-113): This type cannot be encoded.
+// TypeError: (160-164): This type cannot be encoded.
+// TypeError: (166-168): This type cannot be encoded.
+// TypeError: (170-176): 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
new file mode 100644
index 00000000..fa910260
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol
@@ -0,0 +1,13 @@
+contract C {
+ struct S { uint x; }
+ S s;
+ struct T { uint y; }
+ T t;
+ function f() public view {
+ bytes32 a = sha256(s, t);
+ a;
+ }
+}
+// ----
+// 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
new file mode 100644
index 00000000..1187ce4a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol
@@ -0,0 +1,16 @@
+pragma experimental ABIEncoderV2;
+
+contract C {
+ struct S { uint x; }
+ S s;
+ struct T { uint y; }
+ T t;
+ function f() public view {
+ bytes32 a = sha256(s, t);
+ a;
+ }
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// TypeError: (174-175): This type cannot be encoded.
+// TypeError: (177-178): This type cannot be encoded.
diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol
new file mode 100644
index 00000000..d10c1718
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol
@@ -0,0 +1,17 @@
+contract C {
+ struct S { uint x; }
+ S s;
+ struct T { }
+ T t;
+ enum A { X, Y }
+ function f() public pure {
+ bool a = address(this).delegatecall(S, A, A.X, T, uint, uint[]);
+ }
+}
+// ----
+// Warning: (51-63): Defining empty structs is deprecated.
+// TypeError: (168-169): This type cannot be encoded.
+// TypeError: (171-172): This type cannot be encoded.
+// TypeError: (179-180): This type cannot be encoded.
+// TypeError: (182-186): This type cannot be encoded.
+// TypeError: (188-194): 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
new file mode 100644
index 00000000..c8364548
--- /dev/null
+++ b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol
@@ -0,0 +1,9 @@
+contract C {
+ uint[3] sarr;
+ function f() view public {
+ uint[3] memory arr;
+ bytes32 h = keccak256(this.f, arr, sarr);
+ h;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol
new file mode 100644
index 00000000..895bb6c5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol
@@ -0,0 +1,15 @@
+pragma experimental ABIEncoderV2;
+
+contract C {
+ struct T { U u; V v; }
+
+ struct U { W w; }
+
+ struct V { W w; }
+
+ struct W { uint x; }
+
+ function f(T) public pure { }
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol
new file mode 100644
index 00000000..96362ef0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol
@@ -0,0 +1,15 @@
+pragma experimental ABIEncoderV2;
+
+contract TestContract
+{
+ struct SubStruct {
+ uint256 id;
+ }
+ struct TestStruct {
+ SubStruct subStruct1;
+ SubStruct subStruct2;
+ }
+ function addTestStruct(TestStruct) public pure {}
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol
new file mode 100644
index 00000000..4966a731
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol
@@ -0,0 +1,7 @@
+contract C {
+ struct S { uint a; S[] sub; }
+ function f() public pure returns (uint, S) {
+ }
+}
+// ----
+// TypeError: (91-92): Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol
new file mode 100644
index 00000000..68113924
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol
@@ -0,0 +1,7 @@
+contract C {
+ struct S { uint a; S[2][] sub; }
+ function f() public pure returns (uint, S) {
+ }
+}
+// ----
+// TypeError: (94-95): Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol
new file mode 100644
index 00000000..47690d9b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol
@@ -0,0 +1,8 @@
+contract C {
+ struct S { uint a; S[][][] sub; }
+ struct T { S s; }
+ function f() public pure returns (uint x, T t) {
+ }
+}
+// ----
+// TypeError: (119-122): Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol
new file mode 100644
index 00000000..bcffe383
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol
@@ -0,0 +1,8 @@
+contract Test {
+ struct MyStructName {
+ address addr;
+ MyStructName x;
+ }
+}
+// ----
+// TypeError: (20-93): Recursive struct definition.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol
new file mode 100644
index 00000000..64dab8d0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol
@@ -0,0 +1,12 @@
+contract Test {
+ struct MyStructName1 {
+ address addr;
+ uint256 count;
+ MyStructName2 x;
+ }
+ struct MyStructName2 {
+ MyStructName1 x;
+ }
+}
+// ----
+// TypeError: (20-118): Recursive struct definition.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol
new file mode 100644
index 00000000..6ec4ee01
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol
@@ -0,0 +1,4 @@
+contract Test {
+ struct S1 { uint a; }
+ struct S2 { S1 x; S1 y; }
+}
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol
new file mode 100644
index 00000000..926981b3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol
@@ -0,0 +1,7 @@
+contract Test {
+ struct MyStructName1 {
+ address addr;
+ uint256 count;
+ mapping(uint => MyStructName1) x;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/tight_packing_literals.sol b/test/libsolidity/syntaxTests/tight_packing_literals.sol
new file mode 100644
index 00000000..8258a8a6
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tight_packing_literals.sol
@@ -0,0 +1,25 @@
+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);
+ }
+}
+
+// ----
+// 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: (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: (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: (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: (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_fine.sol b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol
new file mode 100644
index 00000000..46407f71
--- /dev/null
+++ b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol
@@ -0,0 +1,11 @@
+contract C {
+ function f() pure public returns (bytes32) {
+ return keccak256(uint8(1));
+ }
+ function g() pure public returns (bytes) {
+ return abi.encode(1);
+ }
+ function h() pure public returns (bytes) {
+ return abi.encodePacked(uint8(1));
+ }
+}
diff --git a/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol
new file mode 100644
index 00000000..45ede998
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol
@@ -0,0 +1,5 @@
+contract c {
+ uint[2**253] data;
+}
+// ----
+// Warning: (17-34): Variable covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol
new file mode 100644
index 00000000..94981aa0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol
@@ -0,0 +1,13 @@
+contract c {
+ function f() public pure {
+ int a;
+ a = 1 << 4095; // shift is fine, but result too large
+ a = 1 << 4096; // too large
+ a = (1E1233) << 2; // too large
+ }
+}
+// ----
+// TypeError: (71-80): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256.
+// TypeError: (133-142): Operator << not compatible with types int_const 1 and int_const 4096
+// TypeError: (169-182): Operator << not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2
+// TypeError: (169-182): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol
new file mode 100644
index 00000000..1b0b5f94
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol
@@ -0,0 +1,9 @@
+contract c {
+ function f() public pure {
+ int a;
+ a = 1/(2<<4094)/(2<<4094);
+ }
+}
+// ----
+// TypeError: (71-92): Operator / not compatible with types rational_const 1 / 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168
+// TypeError: (71-92): Type rational_const 1 / 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. Try converting to type ufixed8x80 or use an explicit conversion.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol
new file mode 100644
index 00000000..6785f580
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol
@@ -0,0 +1,50 @@
+contract c {
+ function f() public pure {
+ int a;
+ a = 4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4;
+ a = -4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4 ** 4;
+ a = 4 ** (-(2 ** 4 ** 4 ** 4 ** 4 ** 4));
+ a = 0 ** 1E1233; // fine
+ a = 1 ** 1E1233; // fine
+ a = -1 ** 1E1233; // fine
+ a = 2 ** 1E1233;
+ a = -2 ** 1E1233;
+ a = 2 ** -1E1233;
+ a = -2 ** -1E1233;
+ a = 1E1233 ** 2;
+ a = -1E1233 ** 2;
+ a = 1E1233 ** -2;
+ a = -1E1233 ** -2;
+ a = 1E1233 ** 1E1233;
+ a = 1E1233 ** -1E1233;
+ a = -1E1233 ** 1E1233;
+ a = -1E1233 ** -1E1233;
+ }
+}
+// ----
+// TypeError: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
+// TypeError: (71-102): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256.
+// TypeError: (116-148): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
+// TypeError: (116-153): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
+// TypeError: (116-153): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256.
+// TypeError: (167-203): Operator ** not compatible with types int_const 4 and int_const -179...(302 digits omitted)...7216
+// TypeError: (317-328): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000
+// TypeError: (342-354): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000
+// TypeError: (368-380): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000
+// TypeError: (394-407): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000
+// TypeError: (421-432): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2
+// TypeError: (421-432): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (446-458): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2
+// TypeError: (446-458): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (472-484): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2
+// TypeError: (472-484): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (498-511): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2
+// TypeError: (498-511): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (525-541): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000
+// TypeError: (525-541): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (555-572): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000
+// TypeError: (555-572): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (586-603): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000
+// TypeError: (586-603): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
+// TypeError: (617-635): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000
+// TypeError: (617-635): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol
new file mode 100644
index 00000000..233857a3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol
@@ -0,0 +1,9 @@
+contract c {
+ function bignum() public {
+ uint256 a;
+ a = 1e1233 / 1e1233; // 1e1233 is still fine
+ a = 1e1234; // 1e1234 is too big
+ }
+}
+// ----
+// TypeError: (128-134): Invalid literal value.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol
new file mode 100644
index 00000000..16673924
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol
@@ -0,0 +1,9 @@
+contract c {
+ function bignum() public {
+ uint a;
+ a = 134562324532464234452335168163516E1200 / 134562324532464234452335168163516E1200; // still fine
+ a = 1345623245324642344523351681635168E1200; // too large
+ }
+}
+// ----
+// TypeError: (179-218): Invalid literal value.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol
new file mode 100644
index 00000000..5a696171
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol
@@ -0,0 +1,9 @@
+contract c {
+ function bignum() public {
+ uint a;
+ a = 134562324532464.234452335168163517E1200 / 134562324532464.234452335168163517E1200; // still fine
+ a = 134562324532464.2344523351681635177E1200; // too large
+ }
+}
+// ----
+// TypeError: (181-221): Invalid literal value.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol
new file mode 100644
index 00000000..bbed94b5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol
@@ -0,0 +1,9 @@
+contract c {
+ function f() public pure {
+ int a;
+ a = (1<<4095)*(1<<4095);
+ }
+}
+// ----
+// TypeError: (71-90): Operator * not compatible with types int_const 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168
+// TypeError: (71-90): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256.
diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol
new file mode 100644
index 00000000..ca7db42e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol
@@ -0,0 +1,8 @@
+contract C {
+ function f() pure public returns (bytes r) {
+ r = abi.encode(1, 2);
+ r = abi.encodePacked(f());
+ r = abi.encodeWithSelector(0x12345678, 1);
+ r = abi.encodeWithSignature("f(uint256)", 4);
+ }
+}
diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol
new file mode 100644
index 00000000..547362c3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol
@@ -0,0 +1,36 @@
+contract C {
+ uint x;
+ function gView() public view returns (uint) { return x; }
+ function gNonPayable() public returns (uint) { x = 4; return 0; }
+
+ function f1() view public returns (bytes) {
+ return abi.encode(gView());
+ }
+ function f2() view public returns (bytes) {
+ return abi.encodePacked(gView());
+ }
+ function f3() view public returns (bytes) {
+ return abi.encodeWithSelector(0x12345678, gView());
+ }
+ function f4() view public returns (bytes) {
+ return abi.encodeWithSignature("f(uint256)", gView());
+ }
+ function g1() public returns (bytes) {
+ return abi.encode(gNonPayable());
+ }
+ function g2() public returns (bytes) {
+ return abi.encodePacked(gNonPayable());
+ }
+ function g3() public returns (bytes) {
+ return abi.encodeWithSelector(0x12345678, gNonPayable());
+ }
+ function g4() public returns (bytes) {
+ return abi.encodeWithSignature("f(uint256)", gNonPayable());
+ }
+ // This will generate the only warning.
+ function check() public returns (bytes) {
+ return abi.encode(2);
+ }
+}
+// ----
+// Warning: (1044-1121): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol b/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol
new file mode 100644
index 00000000..b033fd0c
--- /dev/null
+++ b/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol
@@ -0,0 +1,14 @@
+library WithModifier {
+ modifier mod() { require(msg.value > 10 ether); _; }
+ function withMod(uint self) mod() internal view { require(self > 0); }
+}
+
+contract Test {
+ using WithModifier for *;
+
+ function f(uint _value) public payable {
+ _value.withMod();
+ WithModifier.withMod(_value);
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/visibility/external_library_function.sol b/test/libsolidity/syntaxTests/visibility/external_library_function.sol
new file mode 100644
index 00000000..110e74db
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/external_library_function.sol
@@ -0,0 +1,14 @@
+library L {
+ function f(uint) pure external {}
+}
+
+contract C {
+ using L for *;
+
+ function f() public pure {
+ L.f(2);
+ uint x;
+ x.f();
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol
new file mode 100644
index 00000000..72ce3b40
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol
@@ -0,0 +1,6 @@
+interface I {
+ function f();
+}
+// ----
+// Warning: (15-28): Functions in interfaces should be declared external.
+// Warning: (15-28): No visibility specified. Defaulting to "public". In interfaces it defaults to external.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol
new file mode 100644
index 00000000..513df26b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol
@@ -0,0 +1,7 @@
+pragma experimental "v0.5.0";
+interface I {
+ function f();
+}
+// ----
+// SyntaxError: (45-58): No visibility specified.
+// TypeError: (45-58): Functions in interfaces must be declared external.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol
new file mode 100644
index 00000000..3f0a9aca
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol
@@ -0,0 +1,5 @@
+pragma experimental "v0.5.0";
+interface I {
+ function f() external;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol
new file mode 100644
index 00000000..ac62e69b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol
@@ -0,0 +1,5 @@
+interface I {
+ function f() internal;
+}
+// ----
+// TypeError: (15-37): Functions in interfaces cannot be internal or private.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol
new file mode 100644
index 00000000..881e647e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol
@@ -0,0 +1,5 @@
+interface I {
+ function f() private;
+}
+// ----
+// TypeError: (15-36): Functions in interfaces cannot be internal or private.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol
new file mode 100644
index 00000000..891d9fdf
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol
@@ -0,0 +1,5 @@
+interface I {
+ function f() public;
+}
+// ----
+// Warning: (15-35): Functions in interfaces should be declared external.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol
new file mode 100644
index 00000000..e0c04095
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol
@@ -0,0 +1,6 @@
+pragma experimental "v0.5.0";
+interface I {
+ function f() public;
+}
+// ----
+// TypeError: (45-65): Functions in interfaces must be declared external.
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
new file mode 100644
index 00000000..febb0c26
--- /dev/null
+++ b/test/tools/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_executable(solfuzzer fuzzer.cpp)
+target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_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})
diff --git a/test/fuzzer.cpp b/test/tools/fuzzer.cpp
index 71f38b67..71f38b67 100644
--- a/test/fuzzer.cpp
+++ b/test/tools/fuzzer.cpp
diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp
new file mode 100644
index 00000000..7a147bd0
--- /dev/null
+++ b/test/tools/isoltest.cpp
@@ -0,0 +1,396 @@
+/*
+ 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 <libdevcore/CommonIO.h>
+#include <test/libsolidity/AnalysisFramework.h>
+#include <test/libsolidity/SyntaxTest.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/program_options.hpp>
+
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+#include <queue>
+
+using namespace dev;
+using namespace dev::solidity;
+using namespace dev::solidity::test;
+using namespace dev::solidity::test::formatting;
+using namespace std;
+namespace po = boost::program_options;
+namespace fs = boost::filesystem;
+
+struct SyntaxTestStats
+{
+ int successCount;
+ int runCount;
+ operator bool() const { return successCount == runCount; }
+};
+
+class SyntaxTestTool
+{
+public:
+ SyntaxTestTool(string const& _name, fs::path const& _path, bool _formatted):
+ m_formatted(_formatted), m_name(_name), m_path(_path)
+ {}
+
+ enum class Result
+ {
+ Success,
+ Failure,
+ Exception
+ };
+
+ Result process();
+
+ static SyntaxTestStats processPath(
+ fs::path const& _basepath,
+ fs::path const& _path,
+ bool const _formatted
+ );
+
+ static string editor;
+private:
+ enum class Request
+ {
+ Skip,
+ Rerun,
+ Quit
+ };
+
+ Request handleResponse(bool const _exception);
+
+ void printContract() const;
+
+ bool const m_formatted;
+ string const m_name;
+ fs::path const m_path;
+ unique_ptr<SyntaxTest> m_test;
+};
+
+string SyntaxTestTool::editor;
+
+void SyntaxTestTool::printContract() const
+{
+ if (m_formatted)
+ {
+ string const& source = m_test->source();
+ if (source.empty())
+ return;
+
+ std::vector<char const*> sourceFormatting(source.length(), formatting::RESET);
+ for (auto const& error: m_test->errorList())
+ if (error.locationStart >= 0 && error.locationEnd >= 0)
+ {
+ assert(static_cast<size_t>(error.locationStart) < source.length());
+ assert(static_cast<size_t>(error.locationEnd) < source.length());
+ bool isWarning = error.type == "Warning";
+ for (int i = error.locationStart; i < error.locationEnd; i++)
+ if (isWarning)
+ {
+ if (sourceFormatting[i] == formatting::RESET)
+ sourceFormatting[i] = formatting::ORANGE_BACKGROUND;
+ }
+ else
+ sourceFormatting[i] = formatting::RED_BACKGROUND;
+ }
+
+ cout << " " << sourceFormatting.front() << source.front();
+ for (size_t i = 1; i < source.length(); i++)
+ {
+ if (sourceFormatting[i] != sourceFormatting[i - 1])
+ cout << sourceFormatting[i];
+ if (source[i] != '\n')
+ cout << source[i];
+ else
+ {
+ cout << formatting::RESET << endl;
+ if (i + 1 < source.length())
+ cout << " " << sourceFormatting[i];
+ }
+ }
+ cout << formatting::RESET << endl;
+ }
+ else
+ {
+ stringstream stream(m_test->source());
+ string line;
+ while (getline(stream, line))
+ cout << " " << line << endl;
+ cout << endl;
+ }
+}
+
+SyntaxTestTool::Result SyntaxTestTool::process()
+{
+ bool success;
+ std::stringstream outputMessages;
+
+ (FormattedScope(cout, m_formatted, {BOLD}) << m_name << ": ").flush();
+
+ try
+ {
+ m_test = unique_ptr<SyntaxTest>(new SyntaxTest(m_path.string()));
+ success = m_test->run(outputMessages, " ", m_formatted);
+ }
+ catch(CompilerError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "Exception: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(InternalCompilerError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "InternalCompilerError: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(FatalError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "FatalError: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(UnimplementedFeatureError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "UnimplementedFeatureError: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch (std::exception const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) << "Exception: " << _e.what() << endl;
+ return Result::Exception;
+ }
+ catch(...)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "Unknown Exception" << endl;
+ return Result::Exception;
+ }
+
+ if (success)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, GREEN}) << "OK" << endl;
+ return Result::Success;
+ }
+ else
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) << "FAIL" << endl;
+
+ FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl;
+ printContract();
+
+ cout << outputMessages.str() << endl;
+ return Result::Failure;
+ }
+}
+
+SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception)
+{
+ if (_exception)
+ cout << "(e)dit/(s)kip/(q)uit? ";
+ else
+ cout << "(e)dit/(u)pdate expectations/(s)kip/(q)uit? ";
+ cout.flush();
+
+ while (true)
+ {
+ switch(readStandardInputChar())
+ {
+ case 's':
+ cout << endl;
+ return Request::Skip;
+ case 'u':
+ if (_exception)
+ break;
+ else
+ {
+ cout << endl;
+ ofstream file(m_path.string(), ios::trunc);
+ file << m_test->source();
+ file << "// ----" << endl;
+ if (!m_test->errorList().empty())
+ m_test->printErrorList(file, m_test->errorList(), "// ", false);
+ return Request::Rerun;
+ }
+ case 'e':
+ cout << endl << endl;
+ if (system((editor + " \"" + m_path.string() + "\"").c_str()))
+ cerr << "Error running editor command." << endl << endl;
+ return Request::Rerun;
+ case 'q':
+ cout << endl;
+ return Request::Quit;
+ default:
+ break;
+ }
+ }
+}
+
+
+SyntaxTestStats SyntaxTestTool::processPath(
+ fs::path const& _basepath,
+ fs::path const& _path,
+ bool const _formatted
+)
+{
+ std::queue<fs::path> paths;
+ paths.push(_path);
+ int successCount = 0;
+ int runCount = 0;
+
+ while (!paths.empty())
+ {
+ auto currentPath = paths.front();
+
+ fs::path fullpath = _basepath / currentPath;
+ if (fs::is_directory(fullpath))
+ {
+ paths.pop();
+ for (auto const& entry: boost::iterator_range<fs::directory_iterator>(
+ fs::directory_iterator(fullpath),
+ fs::directory_iterator()
+ ))
+ if (fs::is_directory(entry.path()) || SyntaxTest::isTestFilename(entry.path().filename()))
+ paths.push(currentPath / entry.path().filename());
+ }
+ else
+ {
+ SyntaxTestTool testTool(currentPath.string(), fullpath, _formatted);
+ ++runCount;
+ auto result = testTool.process();
+
+ switch(result)
+ {
+ case Result::Failure:
+ case Result::Exception:
+ switch(testTool.handleResponse(result == Result::Exception))
+ {
+ case Request::Quit:
+ return { successCount, runCount };
+ case Request::Rerun:
+ cout << "Re-running test case..." << endl;
+ --runCount;
+ break;
+ case Request::Skip:
+ paths.pop();
+ break;
+ }
+ break;
+ case Result::Success:
+ paths.pop();
+ ++successCount;
+ break;
+ }
+ }
+ }
+
+ return { successCount, runCount };
+
+}
+
+int main(int argc, char *argv[])
+{
+ if (getenv("EDITOR"))
+ SyntaxTestTool::editor = getenv("EDITOR");
+ else if (fs::exists("/usr/bin/editor"))
+ SyntaxTestTool::editor = "/usr/bin/editor";
+
+ fs::path testPath;
+ bool formatted = true;
+ po::options_description options(
+ R"(isoltest, tool for interactively managing test contracts.
+Usage: isoltest [Options] --testpath path
+Interactively validates test contracts.
+
+Allowed options)",
+ po::options_description::m_default_line_length,
+ po::options_description::m_default_line_length - 23);
+ options.add_options()
+ ("help", "Show this help screen.")
+ ("testpath", po::value<fs::path>(&testPath), "path to test files")
+ ("no-color", "don't use colors")
+ ("editor", po::value<string>(&SyntaxTestTool::editor), "editor for opening contracts");
+
+ po::variables_map arguments;
+ try
+ {
+ po::command_line_parser cmdLineParser(argc, argv);
+ cmdLineParser.options(options);
+ po::store(cmdLineParser.run(), arguments);
+
+ if (arguments.count("help"))
+ {
+ cout << options << endl;
+ return 0;
+ }
+
+ if (arguments.count("no-color"))
+ formatted = false;
+
+ po::notify(arguments);
+ }
+ catch (po::error const& _exception)
+ {
+ cerr << _exception.what() << endl;
+ return 1;
+ }
+
+ if (testPath.empty())
+ {
+ auto const searchPath =
+ {
+ fs::current_path() / ".." / ".." / ".." / "test",
+ fs::current_path() / ".." / ".." / "test",
+ fs::current_path() / ".." / "test",
+ fs::current_path() / "test",
+ fs::current_path()
+ };
+ for (auto const& basePath : searchPath)
+ {
+ fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests";
+ if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath))
+ {
+ testPath = basePath;
+ break;
+ }
+ }
+ }
+
+ fs::path syntaxTestPath = testPath / "libsolidity" / "syntaxTests";
+
+ if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath))
+ {
+ auto stats = SyntaxTestTool::processPath(testPath / "libsolidity", "syntaxTests", formatted);
+
+ cout << endl << "Summary: ";
+ FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) <<
+ stats.successCount << "/" << stats.runCount;
+ cout << " tests successful." << endl;
+
+ return stats ? 0 : 1;
+ }
+ else
+ {
+ cerr << "Test path not found. Use the --testpath argument." << endl;
+ return 1;
+ }
+}