aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-12-20 01:06:13 +0800
committerGitHub <noreply@github.com>2018-12-20 01:06:13 +0800
commit1df8f40cd2fd7b47698d847907b8ca7b47eb488d (patch)
tree5ed5816fe2d1a8a207e750d39884aca7957c8289
parentc8a2cb62832afb2dc09ccee6fd42c1516dfdb981 (diff)
parentddf54b21d1d002903624f61173ab4af197f50053 (diff)
downloaddexon-solidity-1df8f40cd2fd7b47698d847907b8ca7b47eb488d.tar.gz
dexon-solidity-1df8f40cd2fd7b47698d847907b8ca7b47eb488d.tar.zst
dexon-solidity-1df8f40cd2fd7b47698d847907b8ca7b47eb488d.zip
Merge pull request #5697 from ethereum/develop
Merge develop into release for 0.5.2
-rw-r--r--.circleci/config.yml22
-rw-r--r--.travis.yml37
-rw-r--r--CMakeLists.txt2
-rw-r--r--Changelog.md29
-rw-r--r--ReleaseChecklist.md1
-rw-r--r--appveyor.yml8
-rw-r--r--cmake/EthCompilerSettings.cmake4
-rw-r--r--cmake/FindCLN.cmake5
-rw-r--r--cmake/FindCVC4.cmake11
-rw-r--r--cmake/FindGMP.cmake7
-rw-r--r--cmake/FindZ3.cmake9
-rw-r--r--cmake/jsoncpp.cmake5
-rw-r--r--docs/assembly.rst6
-rw-r--r--docs/bugs_by_version.json4
-rw-r--r--docs/contracts.rst2
-rw-r--r--docs/contributing.rst12
-rw-r--r--docs/frequently-asked-questions.rst54
-rw-r--r--docs/installing-solidity.rst4
-rw-r--r--docs/introduction-to-smart-contracts.rst2
-rw-r--r--docs/layout-of-source-files.rst2
-rw-r--r--docs/miscellaneous.rst6
-rw-r--r--docs/solidity-by-example.rst2
-rw-r--r--docs/style-guide.rst61
-rw-r--r--docs/types.rst92
-rw-r--r--docs/using-the-compiler.rst24
-rw-r--r--libdevcore/CMakeLists.txt20
-rw-r--r--libdevcore/CommonData.cpp22
-rw-r--r--libdevcore/CommonData.h22
-rw-r--r--libdevcore/FixedHash.h2
-rw-r--r--libdevcore/IndentedWriter.cpp2
-rw-r--r--libdevcore/IndentedWriter.h4
-rw-r--r--libdevcore/Result.h66
-rw-r--r--libdevcore/StringUtils.cpp2
-rw-r--r--libdevcore/StringUtils.h82
-rw-r--r--libevmasm/Assembly.cpp16
-rw-r--r--libevmasm/Assembly.h2
-rw-r--r--libevmasm/AssemblyItem.cpp10
-rw-r--r--libevmasm/AssemblyItem.h25
-rw-r--r--libevmasm/BlockDeduplicator.cpp14
-rw-r--r--libevmasm/CMakeLists.txt18
-rw-r--r--libevmasm/ControlFlowGraph.cpp2
-rw-r--r--libevmasm/KnownState.cpp4
-rw-r--r--libevmasm/KnownState.h17
-rw-r--r--libevmasm/PeepholeOptimiser.cpp8
-rw-r--r--libevmasm/SemanticInformation.cpp2
-rw-r--r--libevmasm/SimplificationRules.cpp2
-rw-r--r--liblangutil/CMakeLists.txt12
-rw-r--r--liblangutil/CharStream.h6
-rw-r--r--liblangutil/EVMVersion.h2
-rw-r--r--liblangutil/Exceptions.h2
-rw-r--r--liblangutil/ParserBase.cpp4
-rw-r--r--liblangutil/SourceLocation.h8
-rw-r--r--liblangutil/SourceReferenceExtractor.cpp89
-rw-r--r--liblangutil/SourceReferenceExtractor.h75
-rw-r--r--liblangutil/SourceReferenceFormatter.cpp103
-rw-r--r--liblangutil/SourceReferenceFormatter.h26
-rw-r--r--liblll/CMakeLists.txt5
-rw-r--r--liblll/CodeFragment.cpp6
-rw-r--r--liblll/CodeFragment.h2
-rw-r--r--liblll/Compiler.cpp20
-rw-r--r--libsolc/CMakeLists.txt4
-rw-r--r--libsolc/libsolc.h2
-rw-r--r--libsolidity/CMakeLists.txt65
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp3
-rw-r--r--libsolidity/analysis/ContractLevelChecker.cpp13
-rw-r--r--libsolidity/analysis/ContractLevelChecker.h1
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.cpp185
-rw-r--r--libsolidity/analysis/ControlFlowAnalyzer.h9
-rw-r--r--libsolidity/analysis/ControlFlowBuilder.cpp262
-rw-r--r--libsolidity/analysis/ControlFlowBuilder.h31
-rw-r--r--libsolidity/analysis/ControlFlowGraph.cpp91
-rw-r--r--libsolidity/analysis/ControlFlowGraph.h104
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp1
-rw-r--r--libsolidity/analysis/DeclarationContainer.h5
-rw-r--r--libsolidity/analysis/DocStringAnalyser.cpp3
-rw-r--r--libsolidity/analysis/GlobalContext.cpp3
-rw-r--r--libsolidity/analysis/GlobalContext.h8
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp3
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h10
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp7
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp14
-rw-r--r--libsolidity/analysis/ReferencesResolver.h7
-rw-r--r--libsolidity/analysis/SemVerHandler.cpp1
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp29
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp13
-rw-r--r--libsolidity/analysis/TypeChecker.cpp87
-rw-r--r--libsolidity/analysis/TypeChecker.h2
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp7
-rw-r--r--libsolidity/ast/AST.cpp5
-rw-r--r--libsolidity/ast/AST.h8
-rw-r--r--libsolidity/ast/ASTAnnotations.h6
-rw-r--r--libsolidity/ast/ASTForward.h2
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp19
-rw-r--r--libsolidity/ast/ASTJsonConverter.h7
-rw-r--r--libsolidity/ast/ASTPrinter.cpp5
-rw-r--r--libsolidity/ast/ASTPrinter.h2
-rw-r--r--libsolidity/ast/ASTVisitor.h4
-rw-r--r--libsolidity/ast/Types.cpp233
-rw-r--r--libsolidity/ast/Types.h120
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp5
-rw-r--r--libsolidity/codegen/ABIFunctions.h7
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp8
-rw-r--r--libsolidity/codegen/AsmCodeGen.cpp197
-rw-r--r--libsolidity/codegen/AsmCodeGen.h92
-rw-r--r--libsolidity/codegen/Compiler.cpp7
-rw-r--r--libsolidity/codegen/Compiler.h4
-rw-r--r--libsolidity/codegen/CompilerContext.cpp19
-rw-r--r--libsolidity/codegen/CompilerContext.h13
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp6
-rw-r--r--libsolidity/codegen/CompilerUtils.h2
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp137
-rw-r--r--libsolidity/codegen/ContractCompiler.h16
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp28
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h13
-rw-r--r--libsolidity/codegen/LValue.cpp5
-rw-r--r--libsolidity/codegen/LValue.h6
-rw-r--r--libsolidity/formal/CVC4Interface.cpp1
-rw-r--r--libsolidity/formal/CVC4Interface.h1
-rw-r--r--libsolidity/formal/SMTChecker.cpp319
-rw-r--r--libsolidity/formal/SMTChecker.h43
-rw-r--r--libsolidity/formal/SMTLib2Interface.cpp8
-rw-r--r--libsolidity/formal/SMTLib2Interface.h11
-rw-r--r--libsolidity/formal/SMTPortfolio.h3
-rw-r--r--libsolidity/formal/SolverInterface.h21
-rw-r--r--libsolidity/formal/SymbolicTypes.cpp56
-rw-r--r--libsolidity/formal/SymbolicTypes.h8
-rw-r--r--libsolidity/formal/SymbolicVariables.cpp65
-rw-r--r--libsolidity/formal/SymbolicVariables.h63
-rw-r--r--libsolidity/formal/Z3Interface.cpp1
-rw-r--r--libsolidity/formal/Z3Interface.h2
-rw-r--r--libsolidity/interface/ABI.cpp1
-rw-r--r--libsolidity/interface/ABI.h4
-rw-r--r--libsolidity/interface/AssemblyStack.cpp82
-rw-r--r--libsolidity/interface/AssemblyStack.h14
-rw-r--r--libsolidity/interface/CompilerStack.cpp99
-rw-r--r--libsolidity/interface/CompilerStack.h15
-rw-r--r--libsolidity/interface/GasEstimator.cpp19
-rw-r--r--libsolidity/interface/GasEstimator.h6
-rw-r--r--libsolidity/interface/Natspec.cpp3
-rw-r--r--libsolidity/interface/Natspec.h4
-rw-r--r--libsolidity/interface/ReadFile.h4
-rw-r--r--libsolidity/interface/StandardCompiler.cpp225
-rw-r--r--libsolidity/interface/Version.cpp5
-rw-r--r--libsolidity/interface/Version.h2
-rw-r--r--libsolidity/parsing/DocStringParser.cpp20
-rw-r--r--libsolidity/parsing/DocStringParser.h2
-rw-r--r--libsolidity/parsing/Parser.cpp43
-rw-r--r--libsolidity/parsing/Parser.h8
-rw-r--r--libyul/AsmAnalysis.cpp55
-rw-r--r--libyul/AsmAnalysis.h10
-rw-r--r--libyul/AsmCodeGen.cpp163
-rw-r--r--libyul/AsmDataForward.h7
-rw-r--r--libyul/AsmParser.cpp66
-rw-r--r--libyul/AsmParser.h15
-rw-r--r--libyul/AsmPrinter.cpp34
-rw-r--r--libyul/AsmPrinter.h30
-rw-r--r--libyul/CMakeLists.txt59
-rw-r--r--libyul/Dialect.cpp29
-rw-r--r--libyul/Dialect.h66
-rw-r--r--libyul/Exceptions.h1
-rw-r--r--libyul/Object.h2
-rw-r--r--libyul/ObjectParser.cpp4
-rw-r--r--libyul/ObjectParser.h7
-rw-r--r--libyul/YulString.h17
-rw-r--r--libyul/backends/evm/AbstractAssembly.h12
-rw-r--r--libyul/backends/evm/EVMAssembly.cpp21
-rw-r--r--libyul/backends/evm/EVMAssembly.h6
-rw-r--r--libyul/backends/evm/EVMCodeTransform.cpp274
-rw-r--r--libyul/backends/evm/EVMCodeTransform.h99
-rw-r--r--libyul/backends/evm/EVMDialect.cpp141
-rw-r--r--libyul/backends/evm/EVMDialect.h85
-rw-r--r--libyul/backends/evm/EVMObjectCompiler.cpp64
-rw-r--r--libyul/backends/evm/EVMObjectCompiler.h (renamed from libyul/AsmCodeGen.h)41
-rw-r--r--libyul/optimiser/ASTWalker.cpp20
-rw-r--r--libyul/optimiser/ASTWalker.h20
-rw-r--r--libyul/optimiser/DataFlowAnalyzer.cpp12
-rw-r--r--libyul/optimiser/DataFlowAnalyzer.h2
-rw-r--r--libyul/optimiser/ForLoopInitRewriter.cpp11
-rw-r--r--libyul/optimiser/FullInliner.cpp45
-rw-r--r--libyul/optimiser/FullInliner.h19
-rw-r--r--libyul/optimiser/MainFunction.cpp4
-rw-r--r--libyul/optimiser/Metrics.cpp3
-rw-r--r--libyul/optimiser/Metrics.h12
-rw-r--r--libyul/optimiser/RedundantAssignEliminator.h2
-rw-r--r--libyul/optimiser/SSAValueTracker.cpp16
-rw-r--r--libyul/optimiser/SSAValueTracker.h3
-rw-r--r--libyul/optimiser/SimplificationRules.cpp8
-rw-r--r--libyul/optimiser/StructuralSimplifier.cpp138
-rw-r--r--libyul/optimiser/StructuralSimplifier.h49
-rw-r--r--libyul/optimiser/Suite.cpp13
-rw-r--r--libyul/optimiser/Utilities.cpp9
-rw-r--r--libyul/optimiser/Utilities.h3
-rw-r--r--libyul/optimiser/VarDeclInitializer.cpp56
-rw-r--r--libyul/optimiser/VarDeclInitializer.h38
-rw-r--r--libyul/optimiser/VarDeclPropagator.cpp126
-rw-r--r--libyul/optimiser/VarDeclPropagator.h60
-rwxr-xr-xscripts/build.sh28
-rwxr-xr-xscripts/docker_deploy_manual.sh6
-rw-r--r--scripts/release.bat10
-rwxr-xr-xscripts/release_ppa.sh43
-rwxr-xr-xscripts/tests.sh23
-rw-r--r--solc/CommandLineInterface.cpp6
-rw-r--r--solc/CommandLineInterface.h2
-rw-r--r--test/InteractiveTests.h63
-rw-r--r--test/RPCSession.cpp35
-rw-r--r--test/RPCSession.h3
-rw-r--r--test/TestCase.h2
-rw-r--r--test/boostTest.cpp52
-rwxr-xr-xtest/cmdlineTests.sh65
-rw-r--r--test/cmdlineTests/data_storage.sol15
-rw-r--r--test/cmdlineTests/data_storage.sol.args1
-rw-r--r--test/cmdlineTests/data_storage.sol.stdout7
-rw-r--r--test/cmdlineTests/gas_test_dispatch.sol43
-rw-r--r--test/cmdlineTests/gas_test_dispatch.sol.args1
-rw-r--r--test/cmdlineTests/gas_test_dispatch.sol.stdout53
-rw-r--r--test/cmdlineTests/gas_test_dispatch_optimize.sol43
-rw-r--r--test/cmdlineTests/gas_test_dispatch_optimize.sol.args1
-rw-r--r--test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout53
-rw-r--r--test/cmdlineTests/standard.json10
-rw-r--r--test/cmdlineTests/standard.json.exit1
-rw-r--r--test/cmdlineTests/standard.json.stdout1
-rw-r--r--test/cmdlineTests/standard_binaries_requested.json17
-rw-r--r--test/cmdlineTests/standard_binaries_requested.json.stdout1
-rw-r--r--test/cmdlineTests/standard_methodIdentifiersRequested.json17
-rw-r--r--test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout1
-rw-r--r--test/cmdlineTests/standard_only_ast_requested.json17
-rw-r--r--test/cmdlineTests/standard_only_ast_requested.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_key_auxiliary_input.json14
-rw-r--r--test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_key_metadata.json22
-rw-r--r--test/cmdlineTests/standard_wrong_key_metadata.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_key_metadata.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_key_optimizer.json22
-rw-r--r--test/cmdlineTests/standard_wrong_key_optimizer.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_key_optimizer.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_key_root.json11
-rw-r--r--test/cmdlineTests/standard_wrong_key_root.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_key_root.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_key_settings.json22
-rw-r--r--test/cmdlineTests/standard_wrong_key_settings.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_key_settings.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_key_source.json11
-rw-r--r--test/cmdlineTests/standard_wrong_key_source.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_key_source.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input.json11
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout2
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json20
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json23
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_metadata.json19
-rw-r--r--test/cmdlineTests/standard_wrong_type_metadata.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_metadata.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_optimizer.json18
-rw-r--r--test/cmdlineTests/standard_wrong_type_optimizer.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_optimizer.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection.json11
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_contract.json16
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_file.json13
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_output.json16
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_remappings.json17
-rw-r--r--test/cmdlineTests/standard_wrong_type_remappings.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_remappings.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_remappings_entry.json17
-rw-r--r--test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_root.json1
-rw-r--r--test/cmdlineTests/standard_wrong_type_root.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_root.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_settings.json23
-rw-r--r--test/cmdlineTests/standard_wrong_type_settings.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_settings.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_source.json12
-rw-r--r--test/cmdlineTests/standard_wrong_type_source.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_source.json.stdout1
-rw-r--r--test/cmdlineTests/standard_wrong_type_sources.json4
-rw-r--r--test/cmdlineTests/standard_wrong_type_sources.json.exit1
-rw-r--r--test/cmdlineTests/standard_wrong_type_sources.json.stdout1
-rw-r--r--test/cmdlineTests/too_long_line.sol (renamed from test/cmdlineErrorReports/too_long_line.sol)0
-rw-r--r--test/cmdlineTests/too_long_line.sol.err (renamed from test/cmdlineErrorReports/too_long_line.sol.ref)0
-rw-r--r--test/cmdlineTests/too_long_line.sol.exit1
-rw-r--r--test/cmdlineTests/too_long_line_both_sides_short.sol (renamed from test/cmdlineErrorReports/too_long_line_both_sides_short.sol)0
-rw-r--r--test/cmdlineTests/too_long_line_both_sides_short.sol.err (renamed from test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref)0
-rw-r--r--test/cmdlineTests/too_long_line_both_sides_short.sol.exit1
-rw-r--r--test/cmdlineTests/too_long_line_edge_in.sol (renamed from test/cmdlineErrorReports/too_long_line_edge_in.sol)0
-rw-r--r--test/cmdlineTests/too_long_line_edge_in.sol.err (renamed from test/cmdlineErrorReports/too_long_line_edge_in.sol.ref)0
-rw-r--r--test/cmdlineTests/too_long_line_edge_in.sol.exit1
-rw-r--r--test/cmdlineTests/too_long_line_edge_out.sol (renamed from test/cmdlineErrorReports/too_long_line_edge_out.sol)0
-rw-r--r--test/cmdlineTests/too_long_line_edge_out.sol.err (renamed from test/cmdlineErrorReports/too_long_line_edge_out.sol.ref)0
-rw-r--r--test/cmdlineTests/too_long_line_edge_out.sol.exit1
-rw-r--r--test/cmdlineTests/too_long_line_left_short.sol (renamed from test/cmdlineErrorReports/too_long_line_left_short.sol)0
-rw-r--r--test/cmdlineTests/too_long_line_left_short.sol.err (renamed from test/cmdlineErrorReports/too_long_line_left_short.sol.ref)0
-rw-r--r--test/cmdlineTests/too_long_line_left_short.sol.exit1
-rw-r--r--test/cmdlineTests/too_long_line_right_short.sol (renamed from test/cmdlineErrorReports/too_long_line_right_short.sol)0
-rw-r--r--test/cmdlineTests/too_long_line_right_short.sol.err (renamed from test/cmdlineErrorReports/too_long_line_right_short.sol.ref)0
-rw-r--r--test/cmdlineTests/too_long_line_right_short.sol.exit1
-rwxr-xr-xtest/externalTests.sh3
-rw-r--r--test/libdevcore/CommonData.cpp87
-rw-r--r--test/libdevcore/StringUtils.cpp57
-rw-r--r--test/libevmasm/Assembler.cpp6
-rw-r--r--test/libevmasm/Optimiser.cpp2
-rw-r--r--test/liblangutil/SourceLocation.cpp12
-rw-r--r--test/liblll/EndToEndTest.cpp2
-rw-r--r--test/libsolidity/AnalysisFramework.cpp5
-rw-r--r--test/libsolidity/Assembly.cpp26
-rw-r--r--test/libsolidity/GasCosts.cpp132
-rw-r--r--test/libsolidity/GasMeter.cpp16
-rw-r--r--test/libsolidity/InlineAssembly.cpp3
-rw-r--r--test/libsolidity/LibSolc.cpp4
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp5
-rw-r--r--test/libsolidity/SolidityExecutionFramework.h3
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp2
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp12
-rw-r--r--test/libsolidity/SolidityNatspecJSON.cpp2
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp2
-rw-r--r--test/libsolidity/SolidityParser.cpp17
-rw-r--r--test/libsolidity/StandardCompiler.cpp6
-rw-r--r--test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol1
-rw-r--r--test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol1
-rw-r--r--test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol17
-rw-r--r--test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol19
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_1_fail.sol17
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol18
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_loop_5.sol2
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_loop_6.sol5
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol10
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol13
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_3.sol18
-rw-r--r--test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol10
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_1.sol17
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_1_fail.sol14
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_2_fail.sol15
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol4
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol2
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol2
-rw-r--r--test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol2
-rw-r--r--test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol2
-rw-r--r--test/libsolidity/smtCheckerTests/special/many.sol2
-rw-r--r--test/libsolidity/smtCheckerTests/special/msg_data.sol3
-rw-r--r--test/libsolidity/smtCheckerTests/types/bytes_1.sol16
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_1.sol10
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol13
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_2.sol11
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol14
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol14
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_3.sol12
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol14
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol14
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_4.sol12
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_5.sol11
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol18
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol21
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol15
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_equal_keys_1.sol10
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol12
-rw-r--r--test/libsolidity/smtCheckerTests/types/mapping_unsupported_key_type_1.sol17
-rw-r--r--test/libsolidity/smtCheckerTestsJSON/multi.json18
-rw-r--r--test/libsolidity/smtCheckerTestsJSON/simple.json14
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol2
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol2
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol2
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol26
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol10
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol4
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol4
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol4
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol6
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol4
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol2
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol8
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol9
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/functionType.sol8
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fail.sol8
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fine.sol6
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_post_access.sol13
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_pre_access.sol13
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/smoke.sol10
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/struct.sol11
-rw-r--r--test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol10
-rw-r--r--test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol6
-rw-r--r--test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol6
-rw-r--r--test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol6
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol3
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol3
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol10
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol3
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol1
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/empty_function.sol4
-rw-r--r--test/libsolidity/syntaxTests/parsing/external_function.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/function_normal_comments.sol3
-rw-r--r--test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol4
-rw-r--r--test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol4
-rw-r--r--test/libsolidity/syntaxTests/parsing/library_simple.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/modifier_invocation.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol12
-rw-r--r--test/libsolidity/syntaxTests/parsing/no_function_params.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/single_function_param.sol3
-rw-r--r--test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol1
-rw-r--r--test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol3
-rw-r--r--test/libsolidity/syntaxTests/types/address/address_binary_operators.sol15
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_div_limit.sol2
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol14
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_literal_to_fixed_implicit.sol15
-rw-r--r--test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol2
-rw-r--r--test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol1
-rw-r--r--test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol1
-rw-r--r--test/libyul/Common.cpp13
-rw-r--r--test/libyul/Common.h3
-rw-r--r--test/libyul/ObjectCompilerTest.cpp134
-rw-r--r--test/libyul/ObjectCompilerTest.h69
-rw-r--r--test/libyul/Parser.cpp65
-rw-r--r--test/libyul/StackReuseCodegen.cpp353
-rw-r--r--test/libyul/YulOptimizerTest.cpp30
-rw-r--r--test/libyul/YulOptimizerTest.h2
-rw-r--r--test/libyul/objectCompiler/data.yul11
-rw-r--r--test/libyul/objectCompiler/datacopy.yul48
-rw-r--r--test/libyul/objectCompiler/dataoffset_code.yul29
-rw-r--r--test/libyul/objectCompiler/dataoffset_data.yul16
-rw-r--r--test/libyul/objectCompiler/dataoffset_self.yul16
-rw-r--r--test/libyul/objectCompiler/datasize_code.yul29
-rw-r--r--test/libyul/objectCompiler/datasize_data.yul16
-rw-r--r--test/libyul/objectCompiler/datasize_self.yul16
-rw-r--r--test/libyul/objectCompiler/namedObject.yul6
-rw-r--r--test/libyul/objectCompiler/namedObjectCode.yul13
-rw-r--r--test/libyul/objectCompiler/nested_optimizer.yul49
-rw-r--r--test/libyul/objectCompiler/simple.yul13
-rw-r--r--test/libyul/objectCompiler/simple_optimizer.yul23
-rw-r--r--test/libyul/objectCompiler/smoke.yul5
-rw-r--r--test/libyul/objectCompiler/subObject.yul21
-rw-r--r--test/libyul/objectCompiler/subSubObject.yul39
-rw-r--r--test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul23
-rw-r--r--test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul14
-rw-r--r--test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul14
-rw-r--r--test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul14
-rw-r--r--test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul11
-rw-r--r--test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul13
-rw-r--r--test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul48
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/double_inline.yul8
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul19
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/long_names.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul6
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/multi_return.yul4
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul44
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul41
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul6
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/pop_result.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullInliner/simple.yul2
-rw-r--r--test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul288
-rw-r--r--test/libyul/yulOptimizerTests/fullSuite/medium.yul19
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_movable_condition.yul7
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_non_movable_condition.yul6
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol10
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul5
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul10
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul6
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul9
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul6
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/switch_only_default.yul11
-rw-r--r--test/libyul/yulOptimizerTests/structuralSimplifier/switch_to_if.yul11
-rw-r--r--test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul29
-rw-r--r--test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul24
-rw-r--r--test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul14
-rw-r--r--test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul21
-rw-r--r--test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul8
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul17
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul13
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul11
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul10
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul9
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul11
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul12
-rw-r--r--test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul16
-rwxr-xr-xtest/solcjsTests.sh2
-rw-r--r--test/tools/CMakeLists.txt18
-rw-r--r--test/tools/isoltest.cpp50
-rw-r--r--test/tools/yulopti.cpp29
512 files changed, 7656 insertions, 2689 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 6c5f3e85..cec69e31 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -15,7 +15,7 @@ defaults:
mkdir -p build
cd build
[ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON"
- cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS
+ cmake .. -DCMAKE_BUILD_TYPE=Release $CMAKE_OPTIONS
make -j4
- run_tests: &run_tests
name: Tests
@@ -68,7 +68,7 @@ jobs:
- version.txt
test_emscripten_solcjs:
docker:
- - image: trzeci/emscripten:sdk-tag-1.37.21-64bit
+ - image: circleci/node:10
environment:
TERM: xterm
steps:
@@ -78,23 +78,15 @@ jobs:
- run:
name: Install external tests deps
command: |
- apt-get -qq update
- apt-get -qy install netcat curl
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
- export NVM_DIR="/usr/local/nvm"
- [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
- nvm --version
- nvm install 8
node --version
npm --version
- run:
name: Test solcjs
command: |
- . /usr/local/nvm/nvm.sh
test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
test_emscripten_external:
docker:
- - image: trzeci/emscripten:sdk-tag-1.37.21-64bit
+ - image: circleci/node:10
environment:
TERM: xterm
steps:
@@ -104,19 +96,11 @@ jobs:
- run:
name: Install external tests deps
command: |
- apt-get -qq update
- apt-get -qy install netcat curl
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
- export NVM_DIR="/usr/local/nvm"
- [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
- nvm --version
- nvm install 8
node --version
npm --version
- run:
name: External tests
command: |
- . /usr/local/nvm/nvm.sh
test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js
build_x86_linux:
docker:
diff --git a/.travis.yml b/.travis.yml
index d68c79ba..8da17c45 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,6 +55,18 @@ matrix:
env:
- ZIP_SUFFIX=ubuntu-trusty
- SOLC_STOREBYTECODE=On
+ before_install:
+ - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+ - sudo add-apt-repository -y ppa:mhier/libboost-latest
+ - sudo add-apt-repository -y ppa:hvr/z3
+ - sudo apt-get update -qq
+ install:
+ - sudo apt-get install -qq g++-8 gcc-8
+ - sudo apt-get install -qq libboost1.67-dev
+ - sudo apt-get install -qq libleveldb1
+ - sudo apt-get install -qq libz3-dev
+ - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90
+ - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90
- os: linux
dist: trusty
@@ -63,6 +75,18 @@ matrix:
env:
- ZIP_SUFFIX=ubuntu-trusty-clang
- SOLC_STOREBYTECODE=On
+ before_install:
+ - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+ - sudo add-apt-repository -y ppa:mhier/libboost-latest
+ - sudo add-apt-repository -y ppa:hvr/z3
+ - sudo apt-get update -qq
+ install:
+ - sudo apt-get install -qq g++-8 gcc-8
+ - sudo apt-get install -qq libboost1.67-dev
+ - sudo apt-get install -qq libleveldb1
+ - sudo apt-get install -qq libz3-dev
+ - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90
+ - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90
# Docker target, which generates a statically linked alpine image
- os: linux
@@ -159,18 +183,15 @@ cache:
install:
- test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh)
- test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh)
+
+before_script:
# Disable tests unless run on the release branch, on tags or with daily cron
- #- if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" -a "$TRAVIS_EVENT_TYPE" != cron ]; then SOLC_TESTS=Off; fi
- - SOLC_TESTS=Off
+ - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" -a "$TRAVIS_EVENT_TYPE" != cron ]; then SOLC_TESTS=Off; fi
- 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
-
-before_script:
- test $SOLC_EMSCRIPTEN != On || (scripts/build_emscripten.sh)
- test $SOLC_DOCKER != On || (scripts/docker_build.sh)
- - test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE
- && scripts/release.sh $ZIP_SUFFIX
- && scripts/create_source_tarball.sh)
+ - test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE -DBoost_USE_STATIC_LIBS=OFF && scripts/create_source_tarball.sh)
script:
- test $SOLC_EMSCRIPTEN != On -o $SOLC_TESTS != On || (scripts/test_emscripten.sh)
@@ -204,7 +225,7 @@ deploy:
- release
- /^v\d/
# This is the deploy target for the native build (Linux and macOS)
- # which generates ZIPs per commit and the source tarball.
+ # which generates the source tarball.
#
# This runs for each tag that is created and adds the corresponding files.
- provider: releases
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b22ade23..399dd9df 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.5.1")
+set(PROJECT_VERSION "0.5.2")
project(solidity VERSION ${PROJECT_VERSION})
option(LLL "Build LLL" OFF)
diff --git a/Changelog.md b/Changelog.md
index a53cca7e..57c611a3 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,32 @@
+### 0.5.2 (2018-12-19)
+
+Language Features:
+ * Control Flow Graph: Detect every access to uninitialized storage pointers.
+
+
+Compiler Features:
+ * Inline Assembly: Improve error messages around invalid function argument count.
+ * Code Generator: Only check callvalue once if all functions are non-payable.
+ * Code Generator: Use codecopy for string constants more aggressively.
+ * Code Generator: Use binary search for dispatch function if more efficient. The size/speed tradeoff can be tuned using ``--optimize-runs``.
+ * SMTChecker: Support mathematical and cryptographic functions in an uninterpreted way.
+ * SMTChecker: Support one-dimensional mappings.
+ * Standard JSON Interface: Disallow unknown keys in standard JSON input.
+ * Standard JSON Interface: Only run code generation if it has been requested. This could lead to unsupported feature errors only being reported at the point where you request bytecode.
+ * Static Analyzer: Do not warn about unused variables or state mutability for functions with an empty body.
+ * Type Checker: Add an additional reason to be displayed when type conversion fails.
+ * Yul: Support object access via ``datasize``, ``dataoffset`` and ``datacopy`` in standalone assembly mode.
+
+
+Bugfixes:
+ * Standard JSON Interface: Report specific error message for json input errors instead of internal compiler error.
+
+
+Build System:
+ * Replace the trusty PPA build by a static build on cosmic that is used for the trusty package instead.
+ * Remove support for Visual Studio 2015.
+
+
### 0.5.1 (2018-12-03)
Language Features:
diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md
index b84ae4c4..04734544 100644
--- a/ReleaseChecklist.md
+++ b/ReleaseChecklist.md
@@ -10,6 +10,7 @@ Checklist for making a release:
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
- [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page).
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
+ - [ ] Once the ``~ethereum/ubuntu/ethereum-static`` PPA build is finished and published for all platforms (make sure not to do this earlier), copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` while selecting ``Copy existing binaries``.
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``).
- [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash)
- [ ] Update the default version on readthedocs.
diff --git a/appveyor.yml b/appveyor.yml
index 91ea8af7..864c6359 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -32,10 +32,9 @@ branches:
- release
- develop
configuration:
- - RelWithDebInfo
+ - Release
environment:
matrix:
- - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
# This is used for pushing to solidity-test-bytecodes
priv_key:
@@ -63,12 +62,11 @@ install:
before_build:
- if not exist build mkdir build
- cd build
- - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2015" ( cmake -G "Visual Studio 14 2015 Win64" .. -DTESTS=On )
- else ( cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On )
+ - cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On
build_script:
- msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal
- cd %APPVEYOR_BUILD_FOLDER%
- - if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" ( scripts\release.bat %CONFIGURATION% 2017 )
+ - scripts\release.bat %CONFIGURATION% 2017
- ps: $bytecodedir = git show -s --format="%cd-%H" --date=short
test_script:
diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake
index b4cc6656..70505fdb 100644
--- a/cmake/EthCompilerSettings.cmake
+++ b/cmake/EthCompilerSettings.cmake
@@ -25,8 +25,8 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
- # Use ISO C++11 standard language.
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ # Use ISO C++14 standard language.
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
# Enables all the warnings about constructions that some users consider questionable,
# and that are easy to avoid. Also enable some extra warning flags that are not
diff --git a/cmake/FindCLN.cmake b/cmake/FindCLN.cmake
index f2234bb4..0b574ab9 100644
--- a/cmake/FindCLN.cmake
+++ b/cmake/FindCLN.cmake
@@ -1,3 +1,8 @@
find_library(CLN_LIBRARY NAMES cln)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CLN DEFAULT_MSG CLN_LIBRARY)
+
+if(CLN_FOUND AND NOT TARGET CLN::CLN)
+ add_library(CLN::CLN UNKNOWN IMPORTED)
+ set_property(TARGET CLN::CLN PROPERTY IMPORTED_LOCATION ${CLN_LIBRARY})
+endif()
diff --git a/cmake/FindCVC4.cmake b/cmake/FindCVC4.cmake
index 2649d7c7..887b907b 100644
--- a/cmake/FindCVC4.cmake
+++ b/cmake/FindCVC4.cmake
@@ -14,12 +14,19 @@ if (USE_CVC4)
set(CVC4_LIBRARIES ${CVC4_LIBRARY})
if (CLN_FOUND)
- set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${CLN_LIBRARY})
+ set(CVC4_LIBRARIES ${CVC4_LIBRARIES} CLN::CLN)
endif ()
if (GMP_FOUND)
- set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${GMP_LIBRARY})
+ set(CVC4_LIBRARIES ${CVC4_LIBRARIES} GMP::GMP)
endif ()
+
+ if (NOT TARGET CVC4::CVC4)
+ add_library(CVC4::CVC4 UNKNOWN IMPORTED)
+ set_property(TARGET CVC4::CVC4 PROPERTY IMPORTED_LOCATION ${CVC4_LIBRARY})
+ set_property(TARGET CVC4::CVC4 PROPERTY INTERFACE_LINK_LIBRARIES ${CVC4_LIBRARIES})
+ set_property(TARGET CVC4::CVC4 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CVC4_INCLUDE_DIR})
+ endif()
endif()
else()
set(CVC4_FOUND FALSE)
diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake
index 8abe354c..c3a02654 100644
--- a/cmake/FindGMP.cmake
+++ b/cmake/FindGMP.cmake
@@ -1,3 +1,8 @@
-find_library(GMP_LIBRARY NAMES gmp )
+find_library(GMP_LIBRARY NAMES gmp)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY)
+
+if(GMP_FOUND AND NOT TARGET GMP::GMP)
+ add_library(GMP::GMP UNKNOWN IMPORTED)
+ set_property(TARGET GMP::GMP PROPERTY IMPORTED_LOCATION ${GMP_LIBRARY})
+endif()
diff --git a/cmake/FindZ3.cmake b/cmake/FindZ3.cmake
index c017cac2..ad34cbc3 100644
--- a/cmake/FindZ3.cmake
+++ b/cmake/FindZ3.cmake
@@ -1,9 +1,14 @@
if (USE_Z3)
find_path(Z3_INCLUDE_DIR NAMES z3++.h PATH_SUFFIXES z3)
- find_library(Z3_LIBRARY NAMES z3 )
+ find_library(Z3_LIBRARY NAMES z3)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR)
+
+ if (NOT TARGET Z3::Z3)
+ add_library(Z3::Z3 UNKNOWN IMPORTED)
+ set_property(TARGET Z3::Z3 PROPERTY IMPORTED_LOCATION ${Z3_LIBRARY})
+ set_property(TARGET Z3::Z3 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Z3_INCLUDE_DIR})
+ endif()
else()
set(Z3_FOUND FALSE)
endif()
-# TODO: Create IMPORTED library for Z3.
diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake
index ea3218ef..4db7b9c3 100644
--- a/cmake/jsoncpp.cmake
+++ b/cmake/jsoncpp.cmake
@@ -15,9 +15,9 @@ set(JSONCPP_INCLUDE_DIR "${prefix}/include")
# versions used in the CI runs.
if(EMSCRIPTEN)
# Do not include all flags in CMAKE_CXX_FLAGS for emscripten,
- # but only use -std=c++11. Using all flags causes build failures
+ # but only use -std=c++14. Using all flags causes build failures
# at the moment.
- set(JSONCPP_CXX_FLAGS -std=c++11)
+ set(JSONCPP_CXX_FLAGS -std=c++14)
else()
set(JSONCPP_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif()
@@ -51,5 +51,6 @@ ExternalProject_Add(jsoncpp-project
add_library(jsoncpp STATIC IMPORTED)
file(MAKE_DIRECTORY ${JSONCPP_INCLUDE_DIR}) # Must exist.
set_property(TARGET jsoncpp PROPERTY IMPORTED_LOCATION ${JSONCPP_LIBRARY})
+set_property(TARGET jsoncpp PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES ${JSONCPP_INCLUDE_DIR})
set_property(TARGET jsoncpp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${JSONCPP_INCLUDE_DIR})
add_dependencies(jsoncpp jsoncpp-project)
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 5bb9825a..b2f150ca 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -159,8 +159,8 @@ following list can be used as a reference of its opcodes.
If an opcode takes arguments (always from the top of the stack), they are given in parentheses.
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
-Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
-special and all others push exactly one item onto the stack.
+Opcodes marked with ``-`` do not push an item onto the stack (do not return a result),
+those marked with ``*`` are special and all others push exactly one item onto the stack (their "return value").
Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively.
Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception.
@@ -346,7 +346,7 @@ Literals
You can use integer constants by typing them in decimal or hexadecimal notation and an
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
-to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc".
+to add 2 and 3 resulting in 5 and then computes the bitwise ``AND`` with the string "abc".
The final value is assigned to a local variable called ``x``.
Strings are stored left-aligned and cannot be longer than 32 bytes.
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index 658aab9c..438abbdd 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -616,5 +616,9 @@
"0.5.1": {
"bugs": [],
"released": "2018-12-03"
+ },
+ "0.5.2": {
+ "bugs": [],
+ "released": "2018-12-19"
}
} \ No newline at end of file
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 98419430..682cb378 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -1408,7 +1408,7 @@ Interfaces are denoted by their own keyword:
::
- pragma solidity >=0.4.11 <0.6.0;
+ pragma solidity >=0.5.0 <0.6.0;
interface Token {
enum TokenType { Fungible, NonFungible }
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 43b2fd38..85816766 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -61,7 +61,7 @@ New features and bugfixes should be added to the ``Changelog.md`` file: please
follow the style of previous entries, when applicable.
Finally, please make sure you respect the `coding style
-<https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_
+<https://github.com/ethereum/solidity/blob/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.
@@ -83,14 +83,14 @@ internally.
.. note ::
- Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/RelWithDebInfo/soltest.exe -- --no-ipc --no-smt``.
- If you're running this in plain Command Prompt, use ``.\build\test\RelWithDebInfo\soltest.exe -- --no-ipc --no-smt``.
+ Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/Release/soltest.exe -- --no-ipc --no-smt``.
+ If you're running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-ipc --no-smt``.
The option ``--no-smt`` disables the tests that require ``libz3`` and
``--no-ipc`` disables those that require ``aleth``.
If you want to run the ipc tests (that test the semantics of the generated code),
-you need to install `aleth <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/aleth_2018-06-20_artful>`_ and run it in testing mode: ``aleth --test -d /tmp/testeth`` (make sure to rename it).
+you need to install `aleth <https://github.com/ethereum/aleth/releases/download/v1.5.0-alpha.7/aleth-1.5.0-alpha.7-linux-x86_64.tar.gz>`_ and run it in testing mode: ``aleth --db memorydb --test -d /tmp/testeth``.
To run the actual tests, use: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``.
@@ -122,9 +122,9 @@ The CI runs additional tests (including ``solc-js`` and testing third party Soli
.. note ::
- You can not use some versions of ``aleth`` for testing. We suggest using
+ Some versions of ``aleth`` can not be used for testing. We suggest using
the same version that the Solidity continuous integration tests use.
- Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``.
+ Currently the CI uses version ``1.5.0-alpha.7`` of ``aleth``.
Writing and running syntax tests
--------------------------------
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index f3c5b1f7..d263e0c6 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -9,35 +9,6 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_.
Basic Questions
***************
-What is the transaction "payload"?
-==================================
-
-This is just the bytecode "data" sent along with the request.
-
-
-Create a contract that can be killed and return funds
-=====================================================
-
-First, a word of warning: Killing contracts sounds like a good idea, because "cleaning up"
-is always good, but as seen above, it does not really clean up. Furthermore,
-if Ether is sent to removed contracts, the Ether will be forever lost.
-
-If you want to deactivate your contracts, it is preferable to **disable** them by changing some
-internal state which causes all functions to throw. This will make it impossible
-to use the contract and ether sent to the contract will be returned automatically.
-
-Now to answering the question: Inside a constructor, ``msg.sender`` is the
-creator. Save it. Then ``selfdestruct(creator);`` to kill and return funds.
-
-`example <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_
-
-Note that if you ``import "mortal"`` at the top of your contracts and declare
-``contract SomeContract is mortal { ...`` and compile with a compiler that already
-has it (which includes `Remix <https://remix.ethereum.org/>`_), then
-``kill()`` is taken care of for you. Once a contract is "mortal", then you can
-``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my
-examples.
-
If I return an ``enum``, I only get integer values in web3.js. How to get the named values?
===========================================================================================
@@ -45,31 +16,6 @@ Enums are not supported by the ABI, they are just supported by Solidity.
You have to do the mapping yourself for now, we might provide some help
later.
-Can state variables be initialized in-line?
-===========================================
-
-Yes, this is possible for all types (even for structs). However, for arrays it
-should be noted that you must declare them as static memory arrays.
-
-Examples::
-
- pragma solidity >=0.4.0 <0.6.0;
-
- contract C {
- struct S {
- uint a;
- uint b;
- }
-
- S public x = S(1, 2);
- string name = "Ada";
- string[4] adaArr = ["This", "is", "an", "array"];
- }
-
- contract D {
- C c = new C();
- }
-
How do structs work?
====================
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index 2797d8b0..c475d427 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -296,13 +296,13 @@ And for Windows:
This latter set of instructions should result in the creation of
**solidity.sln** in that build directory. Double-clicking on that file
should result in Visual Studio firing up. We suggest building
-**RelWithDebugInfo** configuration, but all others work.
+**Release** configuration, but all others work.
Alternatively, you can build for Windows on the command-line, like so:
.. code-block:: bash
- cmake --build . --config RelWithDebInfo
+ cmake --build . --config Release
CMake options
=============
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index 34ef012e..7daae06a 100644
--- a/docs/introduction-to-smart-contracts.rst
+++ b/docs/introduction-to-smart-contracts.rst
@@ -41,7 +41,7 @@ source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
A contract in the sense of Solidity is a collection of code (its *functions*) and
data (its *state*) that resides at a specific address on the Ethereum
blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of
-type ``uint`` (*u*nsigned *int*eger of *256* bits). You can think of it as a single slot
+type ``uint`` (*u*\nsigned *int*\eger of *256* bits). You can think of it as a single slot
in a database that can be queried and altered by calling functions of the
code that manages the database. In the case of Ethereum, this is always the owning
contract. And in this case, the functions ``set`` and ``get`` can be used to modify
diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst
index ad84b200..fa36fc6a 100644
--- a/docs/layout-of-source-files.rst
+++ b/docs/layout-of-source-files.rst
@@ -267,7 +267,7 @@ Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible
(these are NEL, LS and PS), it will lead to a parser error.
Additionally, there is another type of comment called a natspec comment,
-for which the documentation is not yet written. They are written with a
+which is detailed in the :ref:`style guide<natspec>`. They are written with a
triple slash (``///``) or a double asterisk block(``/** ... */``) and
they should be used directly above function declarations or statements.
You can use `Doxygen <https://en.wikipedia.org/wiki/Doxygen>`_-style tags inside these comments to document
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 017d5b81..5a6f3875 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -224,9 +224,9 @@ for displaying the current position in the source code inside a debugger
or for breakpoint handling.
Both kinds of source mappings use integer identifiers to refer to source files.
-These are regular array indices into a list of source files usually called
-``"sourceList"``, which is part of the combined-json and the output of
-the json / npm compiler.
+The identifier of a source file is stored in
+``output['sources'][sourceName]['id']`` where ``output`` is the output of the
+standard-json compiler interface parsed as JSON.
.. note ::
In the case of instructions that are not associated with any particular source file,
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index d07452c5..0041e80c 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -743,7 +743,7 @@ In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. Signatu
Extracting the Signature Parameters
-----------------------------------
-Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a messy, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section).
+Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, so the first step is to split these parameters apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into component parts is a mess, so we use `inline assembly <assembly>`_ to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section).
Computing the Message Hash
--------------------------
diff --git a/docs/style-guide.rst b/docs/style-guide.rst
index 68fee871..bf1be93e 100644
--- a/docs/style-guide.rst
+++ b/docs/style-guide.rst
@@ -1093,3 +1093,64 @@ General Recommendations
=======================
TODO
+
+.. _natspec:
+
+*******
+NatSpec
+*******
+
+Solidity contracts can have a form of comments that are the basis of the
+Ethereum Natural Language Specification Format.
+
+Add comments above functions or contracts following `doxygen <http://www.doxygen.nl>`_ notation
+of one or multiple lines starting with `///` or a
+multiline comment starting with `/**` and ending with `*/`.
+
+For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
+added looks like the one below::
+
+ pragma solidity >=0.4.0 <0.6.0;
+
+ /// @author The Solidity Team
+ /// @title A simple storage example
+ contract SimpleStorage {
+ uint storedData;
+
+ /// Store `x`.
+ /// @param x the new value to store
+ /// @dev stores the number in the state variable `storedData`
+ function set(uint x) public {
+ storedData = x;
+ }
+
+ /// Return the stored value.
+ /// @dev retrieves the value of the state variable `storedData`
+ /// @return the stored value
+ function get() public view returns (uint) {
+ return storedData;
+ }
+ }
+
+Natspec uses doxygen style tags with some special meaning.
+If no tag is used, then the comment applies to ``@notice``.
+The ``@notice`` tag is the main NatSpec tag and its audience is
+users of the contract who have never seen the source code, so it should make
+as little assumptions about the inner details as possible.
+All tags are optional.
+
++-------------+-------------------------------------------+-------------------------------+
+| Tag | Description | Context |
++=============+===========================================+===============================+
+| ``@title`` | A title that describes the contract | contract, interface |
++-------------+-------------------------------------------+-------------------------------+
+| ``@author`` | The name of the author | contract, interface, function |
++-------------+-------------------------------------------+-------------------------------+
+| ``@notice`` | Explanation of functionality | contract, interface, function |
++-------------+-------------------------------------------+-------------------------------+
+| ``@dev`` | Any extra details | contract, interface, function |
++-------------+-------------------------------------------+-------------------------------+
+| ``@param`` | Parameter type followed by parameter name | function |
++-------------+-------------------------------------------+-------------------------------+
+| ``@return`` | The return value of a contract's function | function |
++-------------+-------------------------------------------+-------------------------------+
diff --git a/docs/types.rst b/docs/types.rst
index f67a6d1a..08fbd7b3 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -19,6 +19,7 @@ on its type. To handle any unexpected values, you should use the :ref:`revert fu
tuple with a second `bool` value denoting success.
.. index:: ! value type, ! type;value
+.. _value-types:
Value Types
===========
@@ -728,6 +729,8 @@ Another example that uses external function types::
.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
+.. _reference-types:
+
Reference Types
===============
@@ -761,14 +764,17 @@ non-persistent area where function arguments are stored, and behaves mostly like
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
data location.
+.. _data-location-assignment:
+
+Data location and assignment behaviour
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
Data locations are not only relevant for persistency of data, but also for the semantics of assignments:
-assignments between storage and memory (or from calldata) always create an independent copy.
-Assignments from memory to memory only create references. This means that changes to one memory variable
-are also visible in all other memory variables that refer to the same data.
-Assignments from storage to a local storage variables also only assign a reference.
-In contrast, all other assignments to storage always copy. Examples for this case
-are assignments to state variables or to members of local variables of storage struct type, even
-if the local variable itself is just a reference.
+
+* Assignments between ``storage`` and ``memory`` (or from ``calldata``) always create an independent copy.
+* Assignments from ``memory`` to ``memory`` only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data.
+* Assignments from ``storage`` to a local storage variable also only assign a reference.
+* All other assignments to ``storage`` always copy. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference.
::
@@ -805,40 +811,50 @@ if the local variable itself is just a reference.
Arrays
------
-Arrays can have a compile-time fixed size or they can be dynamic.
-The are few restrictions for the element, it can also be
-another array, a mapping or a struct. The general restrictions for
-types apply, though, in that mappings can only be used in storage
-and publicly-visible functions need parameters that are :ref:`ABI types <ABI>`.
+Arrays can have a compile-time fixed size, or they can have a dynamic size.
+
+The type of an array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
+and an array of dynamic size as ``T[]``.
+
+For example, an array of 5 dynamic arrays of ``uint`` is written as
+``uint[][5]``. The notation is reversed compared to some other languages. In
+Solidity, ``X[3]`` is always an array containing three elements of type ``X``,
+even if ``X`` is itself an array. This is not the case in other languages such
+as C.
-An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``,
-an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic
-arrays of ``uint`` is ``uint[][5]`` (note that the notation is reversed when
-compared to some other languages). To access the second uint in the
-third dynamic array, you use ``x[2][1]`` (indices are zero-based and
-access works in the opposite way of the declaration, i.e. ``x[2]``
-shaves off one level in the type from the right).
+Indices are zero-based, and access is in the opposite direction of the
+declaration.
-Accessing an array past its end causes a revert. If you want to add
-new elements, you have to use ``.push()`` or increase the ``.length``
-member (see below).
+For example, if you have a variable ``uint[][5] x memory``, you access the
+second ``uint`` in the third dynamic array using ``x[2][1]``, and to access the
+third dynamic array, use ``x[2]``. Again,
+if you have an array ``T[5] a`` for a type ``T`` that can also be an array,
+then ``a[2]`` always has type ``T``.
+
+Array elements can be of any type, including mapping or struct. The general
+restrictions for types apply, in that mappings can only be stored in the
+``storage`` data location and publicly-visible functions need parameters that are :ref:`ABI types <ABI>`.
+
+Accessing an array past its end causes a failing assertion. You can use the ``.push()`` method to append a new element at the end or assign to the ``.length`` :ref:`member <array-members>` to change the size (see below for caveats).
+method or increase the ``.length`` :ref:`member <array-members>` to add elements.
Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``,
but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow
length or index access.
-So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper.
-As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string``
-for arbitrary-length string (UTF-8) data. If you can limit the length to a certain
-number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper.
+
+You should use ``bytes`` over ``byte[]`` because it is cheaper, since ``byte[]`` adds 31 padding bytes between the elements. As a general rule,
+use ``bytes`` for arbitrary-length raw byte data and ``string`` for arbitrary-length
+string (UTF-8) data. If you can limit the length to a certain number of bytes,
+always use one of the value types ``bytes1`` to ``bytes32`` because they are much cheaper.
.. note::
If you want to access the byte-representation of a string ``s``, use
``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind
that you are accessing the low-level bytes of the UTF-8 representation,
- and not the individual characters!
+ and not the individual characters.
It is possible to mark arrays ``public`` and have Solidity create a :ref:`getter <visibility-and-getters>`.
-The numeric index will become a required parameter for the getter.
+The numeric index becomes a required parameter for the getter.
.. index:: ! array;allocating, new
@@ -912,8 +928,10 @@ complications because of how arrays are passed in the ABI.
.. index:: ! array;length, length, push, pop, !array;push, !array;pop
-Members
-^^^^^^^
+.. _array-members:
+
+Array Members
+^^^^^^^^^^^^^
**length**:
Arrays have a ``length`` member that contains their number of elements.
@@ -1120,9 +1138,10 @@ assigning it to a local variable, as in
``campaigns[campaignID].amount = 0``.
.. index:: !mapping
+.. _mapping-types:
-Mappings
---------
+Mapping Types
+=============
You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
The ``_KeyType`` can be any elementary type. This means it can be any of
@@ -1189,7 +1208,14 @@ If ``a`` is an LValue (i.e. a variable or something that can be assigned to), th
delete
------
-``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
+``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is
+equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic
+array of length zero or a static array of the same length with all elements set to their
+initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
+all other elements and the length of the array untouched. This especially means that it leaves
+a gap in the array. If you plan to remove items, a mapping is probably a better choice.
+
+For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``.
diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst
index 9ba6caa5..4749ef1f 100644
--- a/docs/using-the-compiler.rst
+++ b/docs/using-the-compiler.rst
@@ -200,15 +200,27 @@ Input Description
"MyLib": "0x123123..."
}
}
- // The following can be used to select desired outputs.
- // If this field is omitted, then the compiler loads and does type checking, but will not generate any outputs apart from errors.
- // The first level key is the file name and the second is the contract name, where empty contract name refers to the file itself,
- // while the star refers to all of the contracts.
+ // The following can be used to select desired outputs based
+ // on file and contract names.
+ // If this field is omitted, then the compiler loads and does type checking,
+ // but will not generate any outputs apart from errors.
+ // The first level key is the file name and the second level key is the contract name.
+ // An empty contract name is used for outputs that are not tied to a contract
+ // but to the whole source file like the AST.
+ // A star as contract name refers to all contracts in the file.
+ // Similarly, a star as a file name matches all files.
+ // To select all outputs the compiler can possibly generate, use
+ // "outputSelection: { "*": { "*": [ "*" ], "": [ "*" ] } }"
+ // but note that this might slow down the compilation process needlessly.
//
// The available output types are as follows:
- // abi - ABI
+ //
+ // File level (needs empty string as contract name):
// ast - AST of all source files
// legacyAST - legacy AST of all source files
+ //
+ // Contract level (needs the contract name or "*"):
+ // abi - ABI
// devdoc - Developer documentation (natspec)
// userdoc - User documentation (natspec)
// metadata - Metadata
@@ -281,7 +293,7 @@ Output Description
// This contains the file-level outputs. In can be limited/filtered by the outputSelection settings.
sources: {
"sourceFile.sol": {
- // Identifier (used in source maps)
+ // Identifier of the source (used in source maps)
id: 1,
// The AST object
ast: {},
diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt
index 01a8bcc6..e68ac10a 100644
--- a/libdevcore/CMakeLists.txt
+++ b/libdevcore/CMakeLists.txt
@@ -1,18 +1,36 @@
set(sources
+ Algorithms.h
+ Assertions.h
+ boost_multiprecision_number_compare_bug_workaround.hpp
+ Common.h
CommonData.cpp
+ CommonData.h
CommonIO.cpp
+ CommonIO.h
Exceptions.cpp
+ Exceptions.h
+ FixedHash.h
IndentedWriter.cpp
+ IndentedWriter.h
JSON.cpp
+ JSON.h
Keccak256.cpp
+ Keccak256.h
+ Result.h
StringUtils.cpp
+ StringUtils.h
SwarmHash.cpp
+ SwarmHash.h
UTF8.cpp
+ UTF8.h
+ vector_ref.h
+ Visitor.h
Whiskers.cpp
+ Whiskers.h
)
add_library(devcore ${sources})
-target_link_libraries(devcore PRIVATE jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(devcore PUBLIC jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} Threads::Threads)
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
add_dependencies(devcore solidity_BuildInfo.h)
diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp
index 8d2639c9..1b0e9720 100644
--- a/libdevcore/CommonData.cpp
+++ b/libdevcore/CommonData.cpp
@@ -29,6 +29,28 @@
using namespace std;
using namespace dev;
+string dev::toHex(bytes const& _data, HexPrefix _prefix, HexCase _case)
+{
+ std::ostringstream ret;
+ if (_prefix == HexPrefix::Add)
+ ret << "0x";
+
+ int rix = _data.size() - 1;
+ for (uint8_t c: _data)
+ {
+ // switch hex case every four hexchars
+ auto hexcase = std::nouppercase;
+ if (_case == HexCase::Upper)
+ hexcase = std::uppercase;
+ else if (_case == HexCase::Mixed)
+ hexcase = (rix-- & 2) == 0 ? std::nouppercase : std::uppercase;
+
+ ret << std::hex << hexcase << std::setfill('0') << std::setw(2) << size_t(c);
+ }
+
+ return ret.str();
+}
+
int dev::fromHex(char _i, WhenError _throw)
{
if (_i >= '0' && _i <= '9')
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index fedd3af2..7c59c505 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -50,18 +50,18 @@ enum class HexPrefix
DontAdd = 0,
Add = 1,
};
+
+enum class HexCase
+{
+ Lower = 0,
+ Upper = 1,
+ Mixed = 2,
+};
+
/// Convert a series of bytes to the corresponding string of hex duplets.
/// @param _w specifies the width of the first of the elements. Defaults to two - enough to represent a byte.
/// @example toHex("A\x69") == "4169"
-template <class T>
-std::string toHex(T const& _data, int _w = 2, HexPrefix _prefix = HexPrefix::DontAdd)
-{
- std::ostringstream ret;
- unsigned ii = 0;
- for (auto i: _data)
- ret << std::hex << std::setfill('0') << std::setw(ii++ ? 2 : _w) << (int)(typename std::make_unsigned<decltype(i)>::type)i;
- return (_prefix == HexPrefix::Add) ? "0x" + ret.str() : ret.str();
-}
+std::string toHex(bytes const& _data, HexPrefix _prefix = HexPrefix::DontAdd, HexCase _case = HexCase::Lower);
/// Converts a (printable) ASCII hex character into the correspnding integer value.
/// @example fromHex('A') == 10 && fromHex('f') == 15 && fromHex('5') == 5
@@ -153,7 +153,7 @@ inline std::string formatNumber(bigint const& _value)
if (_value < 0)
return "-" + formatNumber(-_value);
if (_value > 0x1000000)
- return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
+ return toHex(toCompactBigEndian(_value), HexPrefix::Add);
else
return _value.str();
}
@@ -161,7 +161,7 @@ inline std::string formatNumber(bigint const& _value)
inline std::string formatNumber(u256 const& _value)
{
if (_value > 0x1000000)
- return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
+ return toHex(toCompactBigEndian(_value), HexPrefix::Add);
else
return _value.str();
}
diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h
index 24b89840..9245d726 100644
--- a/libdevcore/FixedHash.h
+++ b/libdevcore/FixedHash.h
@@ -95,7 +95,7 @@ public:
uint8_t operator[](unsigned _i) const { return m_data[_i]; }
/// @returns the hash as a user-readable hex string.
- std::string hex() const { return toHex(ref()); }
+ std::string hex() const { return toHex(asBytes()); }
/// @returns a mutable byte vector_ref to the object's data.
bytesRef ref() { return bytesRef(m_data.data(), N); }
diff --git a/libdevcore/IndentedWriter.cpp b/libdevcore/IndentedWriter.cpp
index 96aaf0fa..1a85957b 100644
--- a/libdevcore/IndentedWriter.cpp
+++ b/libdevcore/IndentedWriter.cpp
@@ -36,7 +36,7 @@ string IndentedWriter::format() const
void IndentedWriter::newLine()
{
if (!m_lines.back().contents.empty())
- m_lines.push_back({ string(), m_lines.back().indentation });
+ m_lines.emplace_back(Line{string(), m_lines.back().indentation});
}
void IndentedWriter::indent()
diff --git a/libdevcore/IndentedWriter.h b/libdevcore/IndentedWriter.h
index 4ddd87ed..563c4c96 100644
--- a/libdevcore/IndentedWriter.h
+++ b/libdevcore/IndentedWriter.h
@@ -34,8 +34,6 @@ DEV_SIMPLE_EXCEPTION(IndentedWriterError);
class IndentedWriter
{
public:
- explicit IndentedWriter(): m_lines(std::vector<Line>{{std::string(), 0}}) {}
-
// Returns the formatted output.
std::string format() const;
@@ -61,7 +59,7 @@ private:
unsigned indentation;
};
- std::vector<Line> m_lines;
+ std::vector<Line> m_lines{{std::string(), 0}};
};
}
diff --git a/libdevcore/Result.h b/libdevcore/Result.h
new file mode 100644
index 00000000..4f7a063b
--- /dev/null
+++ b/libdevcore/Result.h
@@ -0,0 +1,66 @@
+/*
+ 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 <string>
+
+namespace dev
+{
+
+/// Simple generic result that holds a value and an optional error message.
+/// Results can be implicitly converted to and created from the type of
+/// the value they hold. The class is mainly designed for a result type of
+/// bool or pointer type. The idea is that the default constructed value of
+/// the result type is interpreted as an error value.
+///
+/// Result<bool> check()
+/// {
+/// if (false)
+/// return Result<bool>("Error message.")
+/// return true;
+/// }
+///
+
+template <class ResultType>
+class Result
+{
+public:
+ Result(ResultType _value): Result(_value, std::string{}) {}
+ Result(std::string _message): Result(ResultType{}, std::move(_message)) {}
+
+ /// @{
+ /// @name Wrapper functions
+ /// Wrapper functions that provide implicit conversions to and explicit retrieval of
+ /// the value this result holds.
+ operator ResultType const&() const { return m_value; }
+ ResultType const& get() const { return m_value; }
+ /// @}
+
+ /// @returns the error message (can be empty).
+ std::string const& message() const { return m_message; }
+
+private:
+ explicit Result(ResultType _value, std::string _message):
+ m_value(std::move(_value)),
+ m_message(std::move(_message))
+ {}
+
+ ResultType m_value;
+ std::string m_message;
+};
+
+}
diff --git a/libdevcore/StringUtils.cpp b/libdevcore/StringUtils.cpp
index 50bf7cce..196ac8f7 100644
--- a/libdevcore/StringUtils.cpp
+++ b/libdevcore/StringUtils.cpp
@@ -91,7 +91,7 @@ string dev::quotedAlternativesList(vector<string> const& suggestions)
vector<string> quotedSuggestions;
for (auto& suggestion: suggestions)
- quotedSuggestions.push_back("\"" + suggestion + "\"");
+ quotedSuggestions.emplace_back("\"" + suggestion + "\"");
return joinHumanReadable(quotedSuggestions, ", ", " or ");
}
diff --git a/libdevcore/StringUtils.h b/libdevcore/StringUtils.h
index b02b9d12..64044cb2 100644
--- a/libdevcore/StringUtils.h
+++ b/libdevcore/StringUtils.h
@@ -26,6 +26,8 @@
#include <string>
#include <vector>
+#include <libdevcore/CommonData.h>
+
namespace dev
{
@@ -72,4 +74,84 @@ std::string joinHumanReadable
return result;
}
+/// Formats large numbers to be easily readable by humans.
+/// Returns decimal representation for smaller numbers; hex for large numbers.
+/// "Special" numbers, powers-of-two and powers-of-two minus 1, are returned in
+/// formulaic form like 0x01 * 2**24 - 1.
+/// @a T will typically by unsigned, u160, u256 or bigint.
+/// @param _value to be formatted
+/// @param _useTruncation if true, internal truncation is also applied,
+/// like 0x5555...{+56 more}...5555
+/// @example formatNumber((u256)0x7ffffff)
+template <class T>
+inline std::string formatNumberReadable(
+ T const& _value,
+ bool _useTruncation = false
+)
+{
+ static_assert(
+ std::is_same<bigint, T>::value || !std::numeric_limits<T>::is_signed,
+ "only unsigned types or bigint supported"
+ ); //bigint does not carry sign bit on shift
+
+ // smaller numbers return as decimal
+ if (_value <= 0x1000000)
+ return _value.str();
+
+ HexCase hexcase = HexCase::Mixed;
+ HexPrefix prefix = HexPrefix::Add;
+
+ // when multiple trailing zero bytes, format as N * 2**x
+ int i = 0;
+ T v = _value;
+ for (; (v & 0xff) == 0; v >>= 8)
+ ++i;
+ if (i > 2)
+ {
+ // 0x100 yields 2**8 (N is 1 and redundant)
+ if (v == 1)
+ return "2**" + std::to_string(i * 8);
+ return toHex(toCompactBigEndian(v), prefix, hexcase) +
+ " * 2**" +
+ std::to_string(i * 8);
+ }
+
+ // when multiple trailing FF bytes, format as N * 2**x - 1
+ i = 0;
+ for (v = _value; (v & 0xff) == 0xff; v >>= 8)
+ ++i;
+ if (i > 2)
+ {
+ // 0xFF yields 2**8 - 1 (v is 0 in that case)
+ if (v == 0)
+ return "2**" + std::to_string(i * 8) + " - 1";
+ return toHex(toCompactBigEndian(T(v + 1)), prefix, hexcase) +
+ " * 2**" + std::to_string(i * 8) +
+ " - 1";
+ }
+
+ std::string str = toHex(toCompactBigEndian(_value), prefix, hexcase);
+ if (_useTruncation)
+ {
+ // return as interior-truncated hex.
+ int len = str.size();
+
+ if (len < 24)
+ return str;
+
+ const int initialChars = (prefix == HexPrefix::Add) ? 6 : 4;
+ const int finalChars = 4;
+ int numSkipped = len - initialChars - finalChars;
+
+ return str.substr(0, initialChars) +
+ "...{+" +
+ std::to_string(numSkipped) +
+ " more}..." +
+ str.substr(len-finalChars, len);
+ }
+
+ // otherwise, show whole value.
+ return str;
+}
+
}
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index 29d9846d..231eed93 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -83,7 +83,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i)
{
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
m_deposit += _i.deposit();
- m_items.push_back(_i);
+ m_items.emplace_back(_i);
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
m_items.back().setLocation(m_currentSourceLocation);
return back();
@@ -353,14 +353,14 @@ AssemblyItem Assembly::namedTag(string const& _name)
assertThrow(!_name.empty(), AssemblyException, "Empty named tag.");
if (!m_namedTags.count(_name))
m_namedTags[_name] = size_t(newTag().data());
- return AssemblyItem(Tag, m_namedTags.at(_name));
+ return AssemblyItem{Tag, m_namedTags.at(_name)};
}
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
{
h256 h(dev::keccak256(_identifier));
m_libraries[h] = _identifier;
- return AssemblyItem(PushLibraryAddress, h);
+ return AssemblyItem{PushLibraryAddress, h};
}
Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs)
@@ -415,14 +415,14 @@ map<u256, u256> Assembly::optimiseInternal(
if (_settings.runJumpdestRemover)
{
- JumpdestRemover jumpdestOpt(m_items);
+ JumpdestRemover jumpdestOpt{m_items};
if (jumpdestOpt.optimise(_tagsReferencedFromOutside))
count++;
}
if (_settings.runPeephole)
{
- PeepholeOptimiser peepOpt(m_items);
+ PeepholeOptimiser peepOpt{m_items};
while (peepOpt.optimise())
{
count++;
@@ -433,7 +433,7 @@ map<u256, u256> Assembly::optimiseInternal(
// This only modifies PushTags, we have to run again to actually remove code.
if (_settings.runDeduplicate)
{
- BlockDeduplicator dedup(m_items);
+ BlockDeduplicator dedup{m_items};
if (dedup.deduplicate())
{
tagReplacements.insert(dedup.replacedTags().begin(), dedup.replacedTags().end());
@@ -448,13 +448,13 @@ 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());
+ 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);
+ CommonSubexpressionEliminator eliminator{emptyState};
auto orig = iter;
iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
bool shouldReplace = false;
diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h
index d846b475..5dc6ef0e 100644
--- a/libevmasm/Assembly.h
+++ b/libevmasm/Assembly.h
@@ -45,8 +45,6 @@ using AssemblyPointer = std::shared_ptr<Assembly>;
class Assembly
{
public:
- Assembly() {}
-
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp
index 52f246d1..0043f76b 100644
--- a/libevmasm/AssemblyItem.cpp
+++ b/libevmasm/AssemblyItem.cpp
@@ -110,7 +110,8 @@ int AssemblyItem::returnValues() const
return 1;
case Tag:
return 0;
- default:;
+ default:
+ break;
}
return 0;
}
@@ -135,9 +136,10 @@ bool AssemblyItem::canBeFunctional() const
return true;
case Tag:
return false;
- default:;
+ default:
+ break;
}
- return 0;
+ return false;
}
string AssemblyItem::getJumpTypeAsString() const
@@ -168,7 +170,7 @@ string AssemblyItem::toAssemblyText() const
break;
}
case Push:
- text = toHex(toCompactBigEndian(data(), 1), 1, HexPrefix::Add);
+ text = toHex(toCompactBigEndian(data(), 1), HexPrefix::Add);
break;
case PushString:
text = string("data_") + toHex(data());
diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h
index a7875171..d21be199 100644
--- a/libevmasm/AssemblyItem.h
+++ b/libevmasm/AssemblyItem.h
@@ -57,22 +57,26 @@ class AssemblyItem
public:
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
- AssemblyItem(u256 _push, langutil::SourceLocation const& _location = langutil::SourceLocation()):
- AssemblyItem(Push, _push, _location) { }
- AssemblyItem(solidity::Instruction _i, langutil::SourceLocation const& _location = langutil::SourceLocation()):
+ AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()):
+ AssemblyItem(Push, std::move(_push), std::move(_location)) { }
+ AssemblyItem(solidity::Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()):
m_type(Operation),
m_instruction(_i),
- m_location(_location)
+ m_location(std::move(_location))
{}
- AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation const& _location = langutil::SourceLocation()):
+ AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()):
m_type(_type),
- m_location(_location)
+ m_location(std::move(_location))
{
if (m_type == Operation)
m_instruction = Instruction(uint8_t(_data));
else
- m_data = std::make_shared<u256>(_data);
+ m_data = std::make_shared<u256>(std::move(_data));
}
+ AssemblyItem(AssemblyItem const&) = default;
+ AssemblyItem(AssemblyItem&&) = default;
+ AssemblyItem& operator=(AssemblyItem const&) = default;
+ AssemblyItem& operator=(AssemblyItem&&) = default;
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, data()); }
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, data()); }
@@ -114,6 +118,13 @@ public:
return data() < _other.data();
}
+ /// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
+ bool operator==(Instruction _instr) const
+ {
+ return type() == Operation && instruction() == _instr;
+ }
+ bool operator!=(Instruction _instr) const { return !operator==(_instr); }
+
/// @returns an upper bound for the number of bytes required by this item, assuming that
/// the value of a jump tag takes @a _addressLength bytes.
unsigned bytesRequired(unsigned _addressLength) const;
diff --git a/libevmasm/BlockDeduplicator.cpp b/libevmasm/BlockDeduplicator.cpp
index b7c69531..ca439925 100644
--- a/libevmasm/BlockDeduplicator.cpp
+++ b/libevmasm/BlockDeduplicator.cpp
@@ -41,7 +41,7 @@ bool BlockDeduplicator::deduplicate()
// Virtual tag that signifies "the current block" and which is used to optimise loops.
// We abort if this virtual tag actually exists.
- AssemblyItem pushSelf(PushTag, u256(-4));
+ AssemblyItem pushSelf{PushTag, u256(-4)};
if (
std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
@@ -55,17 +55,17 @@ bool BlockDeduplicator::deduplicate()
// To compare recursive loops, we have to already unify PushTag opcodes of the
// block's own tag.
- AssemblyItem pushFirstTag(pushSelf);
- AssemblyItem pushSecondTag(pushSelf);
+ AssemblyItem pushFirstTag{pushSelf};
+ AssemblyItem pushSecondTag{pushSelf};
if (_i < m_items.size() && m_items.at(_i).type() == Tag)
pushFirstTag = m_items.at(_i).pushTag();
if (_j < m_items.size() && m_items.at(_j).type() == Tag)
pushSecondTag = m_items.at(_j).pushTag();
- BlockIterator first(m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf);
- BlockIterator second(m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf);
- BlockIterator end(m_items.end(), m_items.end());
+ BlockIterator first{m_items.begin() + _i, m_items.end(), &pushFirstTag, &pushSelf};
+ BlockIterator second{m_items.begin() + _j, m_items.end(), &pushSecondTag, &pushSelf};
+ BlockIterator end{m_items.end(), m_items.end()};
if (first != end && (*first).type() == Tag)
++first;
@@ -126,7 +126,7 @@ BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
{
if (it == end)
return *this;
- if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem(Instruction::JUMPI))
+ if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI})
it = end;
else
{
diff --git a/libevmasm/CMakeLists.txt b/libevmasm/CMakeLists.txt
index e0e3389a..42679938 100644
--- a/libevmasm/CMakeLists.txt
+++ b/libevmasm/CMakeLists.txt
@@ -1,20 +1,38 @@
set(sources
Assembly.cpp
+ Assembly.h
AssemblyItem.cpp
+ AssemblyItem.h
BlockDeduplicator.cpp
+ BlockDeduplicator.h
CommonSubexpressionEliminator.cpp
+ CommonSubexpressionEliminator.h
ConstantOptimiser.cpp
+ ConstantOptimiser.h
ControlFlowGraph.cpp
+ ControlFlowGraph.h
+ Exceptions.h
ExpressionClasses.cpp
+ ExpressionClasses.h
GasMeter.cpp
+ GasMeter.h
Instruction.cpp
+ Instruction.h
JumpdestRemover.cpp
+ JumpdestRemover.h
KnownState.cpp
+ KnownState.h
LinkerObject.cpp
+ LinkerObject.h
PathGasMeter.cpp
+ PathGasMeter.h
PeepholeOptimiser.cpp
+ PeepholeOptimiser.h
SemanticInformation.cpp
+ SemanticInformation.h
+ SimplificationRule.h
SimplificationRules.cpp
+ SimplificationRules.h
)
add_library(evmasm ${sources})
diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp
index d62f5436..e82c2903 100644
--- a/libevmasm/ControlFlowGraph.cpp
+++ b/libevmasm/ControlFlowGraph.cpp
@@ -87,7 +87,7 @@ void ControlFlowGraph::splitBlocks()
m_blocks[id].begin = index;
}
if (item.type() == PushTag)
- m_blocks[id].pushedTags.push_back(BlockId(item.data()));
+ m_blocks[id].pushedTags.emplace_back(item.data());
if (SemanticInformation::altersControlFlow(item))
{
m_blocks[id].end = index + 1;
diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp
index a5546e61..d6cc5ddd 100644
--- a/libevmasm/KnownState.cpp
+++ b/libevmasm/KnownState.cpp
@@ -304,7 +304,7 @@ KnownState::StoreOperation KnownState::storeInStorage(
AssemblyItem item(Instruction::SSTORE, _location);
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
- StoreOperation operation(StoreOperation::Storage, _slot, m_sequenceNumber, id);
+ StoreOperation operation{StoreOperation::Storage, _slot, m_sequenceNumber, id};
m_storageContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
@@ -336,7 +336,7 @@ KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, Source
AssemblyItem item(Instruction::MSTORE, _location);
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
- StoreOperation operation(StoreOperation(StoreOperation::Memory, _slot, m_sequenceNumber, id));
+ StoreOperation operation{StoreOperation::Memory, _slot, m_sequenceNumber, id};
m_memoryContent[_slot] = _value;
// increment a second time so that we get unique sequence numbers for writes
m_sequenceNumber++;
diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h
index 3ab1c4b1..3b5e9e7a 100644
--- a/libevmasm/KnownState.h
+++ b/libevmasm/KnownState.h
@@ -74,18 +74,13 @@ public:
struct StoreOperation
{
enum Target { Invalid, Memory, Storage };
- StoreOperation(): target(Invalid), sequenceNumber(-1) {}
- StoreOperation(
- Target _target,
- Id _slot,
- unsigned _sequenceNumber,
- Id _expression
- ): target(_target), slot(_slot), sequenceNumber(_sequenceNumber), expression(_expression) {}
+
bool isValid() const { return target != Invalid; }
- Target target;
- Id slot;
- unsigned sequenceNumber;
- Id expression;
+
+ Target target{Invalid};
+ Id slot{std::numeric_limits<Id>::max()};
+ unsigned sequenceNumber{std::numeric_limits<unsigned>::max()};
+ Id expression{std::numeric_limits<Id>::max()};
};
explicit KnownState(
diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp
index 6d8e1df6..e211026b 100644
--- a/libevmasm/PeepholeOptimiser.cpp
+++ b/libevmasm/PeepholeOptimiser.cpp
@@ -160,8 +160,7 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap, 2>
{
// Remove SWAP1 if following instruction is commutative
if (
- _swap.type() == Operation &&
- _swap.instruction() == Instruction::SWAP1 &&
+ _swap == Instruction::SWAP1 &&
SemanticInformation::isCommutativeOperation(_op)
)
{
@@ -177,7 +176,7 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
{
static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
{
- map<Instruction, Instruction> swappableOps{
+ static map<Instruction, Instruction> const swappableOps{
{ Instruction::LT, Instruction::GT },
{ Instruction::GT, Instruction::LT },
{ Instruction::SLT, Instruction::SGT },
@@ -185,8 +184,7 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
};
if (
- _swap.type() == Operation &&
- _swap.instruction() == Instruction::SWAP1 &&
+ _swap == Instruction::SWAP1 &&
_op.type() == Operation &&
swappableOps.count(_op.instruction())
)
diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp
index 78f3c9c7..2a24a27e 100644
--- a/libevmasm/SemanticInformation.cpp
+++ b/libevmasm/SemanticInformation.cpp
@@ -108,7 +108,7 @@ bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
{
- return _item == AssemblyItem(Instruction::JUMP) || _item == AssemblyItem(Instruction::JUMPI);
+ return _item == Instruction::JUMP || _item == Instruction::JUMPI;
}
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp
index 1dce5f1e..b812cecc 100644
--- a/libevmasm/SimplificationRules.cpp
+++ b/libevmasm/SimplificationRules.cpp
@@ -209,7 +209,7 @@ ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation c
item = _pattern.toAssemblyItem(_location);
}
for (auto const& arg: _pattern.arguments())
- arguments.push_back(ExpressionTemplate(arg, _location));
+ arguments.emplace_back(arg, _location);
}
string ExpressionTemplate::toString() const
diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt
index dfcccfce..b172108b 100644
--- a/liblangutil/CMakeLists.txt
+++ b/liblangutil/CMakeLists.txt
@@ -1,12 +1,24 @@
# Solidity Commons Library (Solidity related sharing bits between libsolidity and libyul)
set(sources
CharStream.cpp
+ CharStream.h
ErrorReporter.cpp
+ ErrorReporter.h
+ EVMVersion.h
Exceptions.cpp
+ Exceptions.h
ParserBase.cpp
+ ParserBase.h
Scanner.cpp
+ Scanner.h
+ SourceLocation.h
+ SourceReferenceExtractor.cpp
+ SourceReferenceExtractor.h
SourceReferenceFormatter.cpp
+ SourceReferenceFormatter.h
Token.cpp
+ Token.h
+ UndefMacros.h
)
add_library(langutil ${sources})
diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h
index f92beb30..0c998b2a 100644
--- a/liblangutil/CharStream.h
+++ b/liblangutil/CharStream.h
@@ -67,9 +67,9 @@ namespace langutil
class CharStream
{
public:
- CharStream(): m_position(0) {}
+ CharStream() = default;
explicit CharStream(std::string const& _source, std::string const& name):
- m_source(_source), m_name(name), m_position(0) {}
+ m_source(_source), m_name(name) {}
int position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
@@ -94,7 +94,7 @@ public:
private:
std::string m_source;
std::string m_name;
- size_t m_position;
+ size_t m_position{0};
};
}
diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h
index 657727ac..21889bd9 100644
--- a/liblangutil/EVMVersion.h
+++ b/liblangutil/EVMVersion.h
@@ -39,7 +39,7 @@ class EVMVersion:
boost::equality_comparable<EVMVersion>
{
public:
- EVMVersion() {}
+ EVMVersion() = default;
static EVMVersion homestead() { return {Version::Homestead}; }
static EVMVersion tangerineWhistle() { return {Version::TangerineWhistle}; }
diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h
index 5ad31ab2..22deb058 100644
--- a/liblangutil/Exceptions.h
+++ b/liblangutil/Exceptions.h
@@ -105,7 +105,7 @@ class SecondarySourceLocation
public:
SecondarySourceLocation& append(std::string const& _errMsg, SourceLocation const& _sourceLocation)
{
- infos.push_back(std::make_pair(_errMsg, _sourceLocation));
+ infos.emplace_back(_errMsg, _sourceLocation);
return *this;
}
diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp
index 391af291..c0770e04 100644
--- a/liblangutil/ParserBase.cpp
+++ b/liblangutil/ParserBase.cpp
@@ -100,10 +100,10 @@ void ParserBase::decreaseRecursionDepth()
void ParserBase::parserError(string const& _description)
{
- m_errorReporter.parserError(SourceLocation(position(), endPosition(), source()), _description);
+ m_errorReporter.parserError(SourceLocation{position(), endPosition(), source()}, _description);
}
void ParserBase::fatalParserError(string const& _description)
{
- m_errorReporter.fatalParserError(SourceLocation(position(), endPosition(), source()), _description);
+ m_errorReporter.fatalParserError(SourceLocation{position(), endPosition(), source()}, _description);
}
diff --git a/liblangutil/SourceLocation.h b/liblangutil/SourceLocation.h
index 2d18a7d1..840891c2 100644
--- a/liblangutil/SourceLocation.h
+++ b/liblangutil/SourceLocation.h
@@ -38,10 +38,6 @@ namespace langutil
*/
struct SourceLocation
{
- SourceLocation(): start(-1), end(-1), source{nullptr} { }
- SourceLocation(int _start, int _end, std::shared_ptr<CharStream> _source):
- start(_start), end(_end), source{std::move(_source)} { }
-
bool operator==(SourceLocation const& _other) const
{
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
@@ -53,8 +49,8 @@ struct SourceLocation
bool isEmpty() const { return start == -1 && end == -1; }
- int start;
- int end;
+ int start = -1;
+ int end = -1;
std::shared_ptr<CharStream> source;
};
diff --git a/liblangutil/SourceReferenceExtractor.cpp b/liblangutil/SourceReferenceExtractor.cpp
new file mode 100644
index 00000000..4502bb23
--- /dev/null
+++ b/liblangutil/SourceReferenceExtractor.cpp
@@ -0,0 +1,89 @@
+/*
+ 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 <liblangutil/SourceReferenceExtractor.h>
+#include <liblangutil/CharStream.h>
+#include <liblangutil/Exceptions.h>
+
+#include <cmath>
+#include <iomanip>
+
+using namespace std;
+using namespace dev;
+using namespace langutil;
+
+SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Exception const& _exception, string _category)
+{
+ SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
+
+ string const* message = boost::get_error_info<errinfo_comment>(_exception);
+ SourceReference primary = extract(location, message ? *message : "");
+
+ std::vector<SourceReference> secondary;
+ auto secondaryLocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
+ if (secondaryLocation && !secondaryLocation->infos.empty())
+ for (auto const& info: secondaryLocation->infos)
+ secondary.emplace_back(extract(&info.second, info.first));
+
+ return Message{std::move(primary), _category, std::move(secondary)};
+}
+
+SourceReference SourceReferenceExtractor::extract(SourceLocation const* _location, std::string message)
+{
+ if (!_location || !_location->source.get()) // Nothing we can extract here
+ return SourceReference::MessageOnly(std::move(message));
+
+ shared_ptr<CharStream> const& source = _location->source;
+
+ LineColumn const interest = source->translatePositionToLineColumn(_location->start);
+ LineColumn start = interest;
+ LineColumn end = source->translatePositionToLineColumn(_location->end);
+ bool const isMultiline = start.line != end.line;
+
+ string line = source->lineAtPosition(_location->start);
+
+ int locationLength = isMultiline ? line.length() - start.column : end.column - start.column;
+ if (locationLength > 150)
+ {
+ line = line.substr(0, start.column + 35) + " ... " + line.substr(end.column - 35);
+ end.column = start.column + 75;
+ locationLength = 75;
+ }
+
+ if (line.length() > 150)
+ {
+ int const len = line.length();
+ line = line.substr(max(0, start.column - 35), min(start.column, 35) + min(locationLength + 35, len - start.column));
+ if (start.column + locationLength + 35 < len)
+ line += " ...";
+ if (start.column > 35)
+ {
+ line = " ... " + line;
+ start.column = 40;
+ }
+ end.column = start.column + locationLength;
+ }
+
+ return SourceReference{
+ std::move(message),
+ source->name(),
+ interest,
+ isMultiline,
+ line,
+ start.column,
+ end.column,
+ };
+}
diff --git a/liblangutil/SourceReferenceExtractor.h b/liblangutil/SourceReferenceExtractor.h
new file mode 100644
index 00000000..bcbc50bc
--- /dev/null
+++ b/liblangutil/SourceReferenceExtractor.h
@@ -0,0 +1,75 @@
+/*
+ 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 <iosfwd>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace dev
+{
+struct Exception;
+}
+
+namespace langutil
+{
+
+struct LineColumn
+{
+ int line = {-1};
+ int column = {-1};
+
+ LineColumn() = default;
+ LineColumn(std::tuple<int, int> const& _t): line{std::get<0>(_t)}, column{std::get<1>(_t)} {}
+};
+
+struct SourceReference
+{
+ std::string message; ///< A message that relates to this source reference (such as a warning or an error message).
+ std::string sourceName; ///< Underlying source name (for example the filename).
+ LineColumn position; ///< Actual (error) position this source reference is surrounding.
+ bool multiline = {false}; ///< Indicates whether the actual SourceReference is truncated to one line.
+ std::string text; ///< Extracted source code text (potentially truncated if multiline or too long).
+ int startColumn = {-1}; ///< Highlighting range-start of text field.
+ int endColumn = {-1}; ///< Highlighting range-end of text field.
+
+ /// Constructs a SourceReference containing a message only.
+ static SourceReference MessageOnly(std::string _msg)
+ {
+ SourceReference sref;
+ sref.message = std::move(_msg);
+ return sref;
+ }
+};
+
+struct SourceLocation;
+
+namespace SourceReferenceExtractor
+{
+ struct Message
+ {
+ SourceReference primary;
+ std::string category; // "Error", "Warning", ...
+ std::vector<SourceReference> secondary;
+ };
+
+ Message extract(dev::Exception const& _exception, std::string _category);
+ SourceReference extract(SourceLocation const* _location, std::string message = "");
+}
+
+}
diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp
index 8ac05b4e..4bc47a65 100644
--- a/liblangutil/SourceReferenceFormatter.cpp
+++ b/liblangutil/SourceReferenceFormatter.cpp
@@ -30,100 +30,63 @@ using namespace langutil;
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
{
- if (!_location || !_location->source)
- return; // Nothing we can print here
- auto const& scanner = m_scannerFromSourceName(_location->source->name());
- int startLine;
- int startColumn;
- tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
- int endLine;
- int endColumn;
- tie(endLine, endColumn) = scanner.translatePositionToLineColumn(_location->end);
- if (startLine == endLine)
- {
- string line = scanner.lineAtPosition(_location->start);
+ printSourceLocation(SourceReferenceExtractor::extract(_location));
+}
- int locationLength = endColumn - startColumn;
- if (locationLength > 150)
- {
- line = line.substr(0, startColumn + 35) + " ... " + line.substr(endColumn - 35);
- endColumn = startColumn + 75;
- locationLength = 75;
- }
- if (line.length() > 150)
- {
- int len = line.length();
- line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
- if (startColumn + locationLength + 35 < len)
- line += " ...";
- if (startColumn > 35)
- {
- line = " ... " + line;
- startColumn = 40;
- }
- endColumn = startColumn + locationLength;
- }
+void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref)
+{
+ if (_ref.position.line < 0)
+ return; // Nothing we can print here
- m_stream << line << endl;
+ if (!_ref.multiline)
+ {
+ m_stream << _ref.text << endl;
+ // mark the text-range like this: ^-----^
for_each(
- line.cbegin(),
- line.cbegin() + startColumn,
- [this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
+ _ref.text.cbegin(),
+ _ref.text.cbegin() + _ref.startColumn,
+ [this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); }
);
m_stream << "^";
- if (endColumn > startColumn + 2)
- m_stream << string(endColumn - startColumn - 2, '-');
- if (endColumn > startColumn + 1)
+ if (_ref.endColumn > _ref.startColumn + 2)
+ m_stream << string(_ref.endColumn - _ref.startColumn - 2, '-');
+ if (_ref.endColumn > _ref.startColumn + 1)
m_stream << "^";
m_stream << endl;
}
else
m_stream <<
- scanner.lineAtPosition(_location->start) <<
+ _ref.text <<
endl <<
- string(startColumn, ' ') <<
+ string(_ref.startColumn, ' ') <<
"^ (Relevant source part starts here and spans across multiple lines)." <<
endl;
}
-void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
+void SourceReferenceFormatter::printSourceName(SourceReference const& _ref)
{
- if (!_location || !_location->source)
- return; // Nothing we can print here
- auto const& scanner = m_scannerFromSourceName(_location->source->name());
- int startLine;
- int startColumn;
- tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start);
- m_stream << _location->source->name() << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": ";
+ if (_ref.position.line != -1)
+ m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": ";
}
-void SourceReferenceFormatter::printExceptionInformation(
- dev::Exception const& _exception,
- string const& _name
-)
+void SourceReferenceFormatter::printExceptionInformation(dev::Exception const& _error, std::string const& _category)
{
- SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
- auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception);
+ printExceptionInformation(SourceReferenceExtractor::extract(_error, _category));
+}
- printSourceName(location);
+void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtractor::Message const& _msg)
+{
+ printSourceName(_msg.primary);
- m_stream << _name;
- if (string const* description = boost::get_error_info<errinfo_comment>(_exception))
- m_stream << ": " << *description << endl;
- else
- m_stream << endl;
+ m_stream << _msg.category << ": " << _msg.primary.message << endl;
- printSourceLocation(location);
+ printSourceLocation(_msg.primary);
- if (secondarylocation && !secondarylocation->infos.empty())
+ for (auto const& ref: _msg.secondary)
{
- for (auto info: secondarylocation->infos)
- {
- printSourceName(&info.second);
- m_stream << info.first << endl;
- printSourceLocation(&info.second);
- }
- m_stream << endl;
+ printSourceName(ref);
+ m_stream << ref.message << endl;
+ printSourceLocation(ref);
}
}
diff --git a/liblangutil/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h
index 0ef3ca00..9f05f430 100644
--- a/liblangutil/SourceReferenceFormatter.h
+++ b/liblangutil/SourceReferenceFormatter.h
@@ -25,6 +25,7 @@
#include <ostream>
#include <sstream>
#include <functional>
+#include <liblangutil/SourceReferenceExtractor.h>
namespace dev
{
@@ -39,38 +40,33 @@ class Scanner;
class SourceReferenceFormatter
{
public:
- using ScannerFromSourceNameFun = std::function<langutil::Scanner const&(std::string const&)>;
-
- explicit SourceReferenceFormatter(
- std::ostream& _stream,
- ScannerFromSourceNameFun _scannerFromSourceName
- ):
- m_stream(_stream),
- m_scannerFromSourceName(std::move(_scannerFromSourceName))
+ explicit SourceReferenceFormatter(std::ostream& _stream):
+ m_stream(_stream)
{}
/// Prints source location if it is given.
- void printSourceLocation(langutil::SourceLocation const* _location);
- void printExceptionInformation(dev::Exception const& _exception, std::string const& _name);
+ void printSourceLocation(SourceLocation const* _location);
+ void printSourceLocation(SourceReference const& _ref);
+ void printExceptionInformation(dev::Exception const& _error, std::string const& _category);
+ void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
static std::string formatExceptionInformation(
dev::Exception const& _exception,
- std::string const& _name,
- ScannerFromSourceNameFun const& _scannerFromSourceName
+ std::string const& _name
)
{
std::ostringstream errorOutput;
- SourceReferenceFormatter formatter(errorOutput, _scannerFromSourceName);
+ SourceReferenceFormatter formatter(errorOutput);
formatter.printExceptionInformation(_exception, _name);
return errorOutput.str();
}
+
private:
/// Prints source name if location is given.
- void printSourceName(langutil::SourceLocation const* _location);
+ void printSourceName(SourceReference const& _ref);
std::ostream& m_stream;
- ScannerFromSourceNameFun m_scannerFromSourceName;
};
}
diff --git a/liblll/CMakeLists.txt b/liblll/CMakeLists.txt
index 9566c62f..c529461b 100644
--- a/liblll/CMakeLists.txt
+++ b/liblll/CMakeLists.txt
@@ -1,8 +1,13 @@
set(sources
CodeFragment.cpp
+ CodeFragment.h
Compiler.cpp
+ Compiler.h
CompilerState.cpp
+ CompilerState.h
+ Exceptions.h
Parser.cpp
+ Parser.h
)
add_library(lll ${sources})
diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp
index b32f14e9..63d8da3d 100644
--- a/liblll/CodeFragment.cpp
+++ b/liblll/CodeFragment.cpp
@@ -353,7 +353,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
if (j.tag() || j.which() != sp::utree_type::symbol_type)
error<InvalidMacroArgs>();
auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
- args.push_back(string(sr.begin(), sr.end()));
+ args.emplace_back(sr.begin(), sr.end());
}
else if (ii == 3)
{
@@ -464,9 +464,9 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
if (c++)
{
if (us == "LLL" && c == 1)
- code.push_back(CodeFragment(i, ns, m_readFile));
+ code.emplace_back(i, ns, m_readFile);
else
- code.push_back(CodeFragment(i, _s, m_readFile));
+ code.emplace_back(i, _s, m_readFile);
}
auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); };
auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h
index e6e4d3b6..5c2f49a6 100644
--- a/liblll/CodeFragment.h
+++ b/liblll/CodeFragment.h
@@ -41,7 +41,7 @@ class CodeFragment
public:
using ReadCallback = std::function<std::string(std::string const&)>;
- CodeFragment() {}
+ CodeFragment() = default;
CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false);
static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile);
diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp
index f944adbd..6296cbcf 100644
--- a/liblll/Compiler.cpp
+++ b/liblll/Compiler.cpp
@@ -46,22 +46,22 @@ bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer
{
if (_errors)
{
- _errors->push_back("Parse error.");
- _errors->push_back(boost::diagnostic_information(_e));
+ _errors->emplace_back("Parse error.");
+ _errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (std::exception const& _e)
{
if (_errors)
{
- _errors->push_back("Parse exception.");
- _errors->push_back(boost::diagnostic_information(_e));
+ _errors->emplace_back("Parse exception.");
+ _errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (...)
{
if (_errors)
- _errors->push_back("Internal compiler exception.");
+ _errors->emplace_back("Internal compiler exception.");
}
return bytes();
}
@@ -84,22 +84,22 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe
{
if (_errors)
{
- _errors->push_back("Parse error.");
- _errors->push_back(boost::diagnostic_information(_e));
+ _errors->emplace_back("Parse error.");
+ _errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (std::exception const& _e)
{
if (_errors)
{
- _errors->push_back("Parse exception.");
- _errors->push_back(boost::diagnostic_information(_e));
+ _errors->emplace_back("Parse exception.");
+ _errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (...)
{
if (_errors)
- _errors->push_back("Internal compiler exception.");
+ _errors->emplace_back("Internal compiler exception.");
}
return string();
}
diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt
index ed14e9fe..dfdf8162 100644
--- a/libsolc/CMakeLists.txt
+++ b/libsolc/CMakeLists.txt
@@ -1,9 +1,9 @@
if (EMSCRIPTEN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20")
- add_executable(soljson libsolc.cpp)
+ add_executable(soljson libsolc.cpp libsolc.h)
target_link_libraries(soljson PRIVATE solidity)
else()
- add_library(libsolc libsolc.cpp)
+ add_library(libsolc libsolc.cpp libsolc.h)
set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc)
target_link_libraries(libsolc PRIVATE solidity)
endif()
diff --git a/libsolc/libsolc.h b/libsolc/libsolc.h
index 4b0ec639..b58ee805 100644
--- a/libsolc/libsolc.h
+++ b/libsolc/libsolc.h
@@ -20,6 +20,8 @@
* Public compiler API.
*/
+#pragma once
+
#include <stdbool.h>
#ifdef __cplusplus
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index dc4c6d15..8c2ab347 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -1,68 +1,121 @@
# Until we have a clear separation, libyul has to be included here
set(sources
analysis/ConstantEvaluator.cpp
+ analysis/ConstantEvaluator.h
analysis/ContractLevelChecker.cpp
+ analysis/ContractLevelChecker.h
analysis/ControlFlowAnalyzer.cpp
+ analysis/ControlFlowAnalyzer.h
analysis/ControlFlowBuilder.cpp
+ analysis/ControlFlowBuilder.h
analysis/ControlFlowGraph.cpp
+ analysis/ControlFlowGraph.h
analysis/DeclarationContainer.cpp
+ analysis/DeclarationContainer.h
analysis/DocStringAnalyser.cpp
+ analysis/DocStringAnalyser.h
analysis/GlobalContext.cpp
+ analysis/GlobalContext.h
analysis/NameAndTypeResolver.cpp
+ analysis/NameAndTypeResolver.h
analysis/PostTypeChecker.cpp
+ analysis/PostTypeChecker.h
analysis/ReferencesResolver.cpp
+ analysis/ReferencesResolver.h
analysis/SemVerHandler.cpp
+ analysis/SemVerHandler.h
analysis/StaticAnalyzer.cpp
+ analysis/StaticAnalyzer.h
analysis/SyntaxChecker.cpp
+ analysis/SyntaxChecker.h
analysis/TypeChecker.cpp
+ analysis/TypeChecker.h
analysis/ViewPureChecker.cpp
+ analysis/ViewPureChecker.h
ast/AST.cpp
+ ast/AST.h
+ ast/AST_accept.h
ast/ASTAnnotations.cpp
+ ast/ASTAnnotations.h
+ ast/ASTEnums.h
+ ast/ASTForward.h
ast/ASTJsonConverter.cpp
+ ast/ASTJsonConverter.h
ast/ASTPrinter.cpp
+ ast/ASTPrinter.h
+ ast/ASTVisitor.h
+ ast/ExperimentalFeatures.h
ast/Types.cpp
+ ast/Types.h
codegen/ABIFunctions.cpp
+ codegen/ABIFunctions.h
codegen/ArrayUtils.cpp
+ codegen/ArrayUtils.h
+ codegen/AsmCodeGen.cpp
+ codegen/AsmCodeGen.h
codegen/Compiler.cpp
+ codegen/Compiler.h
codegen/CompilerContext.cpp
+ codegen/CompilerContext.h
codegen/CompilerUtils.cpp
+ codegen/CompilerUtils.h
codegen/ContractCompiler.cpp
+ codegen/ContractCompiler.h
codegen/ExpressionCompiler.cpp
+ codegen/ExpressionCompiler.h
codegen/LValue.cpp
+ codegen/LValue.h
formal/SMTChecker.cpp
+ formal/SMTChecker.h
formal/SMTLib2Interface.cpp
+ formal/SMTLib2Interface.h
formal/SMTPortfolio.cpp
+ formal/SMTPortfolio.h
+ formal/SolverInterface.h
formal/SSAVariable.cpp
+ formal/SSAVariable.h
formal/SymbolicTypes.cpp
+ formal/SymbolicTypes.h
formal/SymbolicVariables.cpp
+ formal/SymbolicVariables.h
formal/VariableUsage.cpp
+ formal/VariableUsage.h
interface/ABI.cpp
+ interface/ABI.h
interface/AssemblyStack.cpp
+ interface/AssemblyStack.h
interface/CompilerStack.cpp
+ interface/CompilerStack.h
interface/GasEstimator.cpp
+ interface/GasEstimator.h
interface/Natspec.cpp
+ interface/Natspec.h
+ interface/ReadFile.h
interface/StandardCompiler.cpp
+ interface/StandardCompiler.h
interface/Version.cpp
+ interface/Version.h
parsing/DocStringParser.cpp
+ parsing/DocStringParser.h
parsing/Parser.cpp
+ parsing/Parser.h
+ parsing/Token.h
)
find_package(Z3 QUIET)
if (${Z3_FOUND})
- include_directories(${Z3_INCLUDE_DIR})
add_definitions(-DHAVE_Z3)
message("Z3 SMT solver found. This enables optional SMT checking with Z3.")
- set(z3_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp")
+ set(z3_SRCS formal/Z3Interface.cpp formal/Z3Interface.h)
else()
set(z3_SRCS)
endif()
find_package(CVC4 QUIET)
if (${CVC4_FOUND})
- include_directories(${CVC4_INCLUDE_DIR})
add_definitions(-DHAVE_CVC4)
message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.")
- set(cvc4_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp")
+ set(cvc4_SRCS formal/CVC4Interface.cpp formal/CVC4Interface.h)
else()
set(cvc4_SRCS)
endif()
@@ -76,9 +129,9 @@ add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY})
if (${Z3_FOUND})
- target_link_libraries(solidity PUBLIC ${Z3_LIBRARY})
+ target_link_libraries(solidity PUBLIC Z3::Z3)
endif()
if (${CVC4_FOUND})
- target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES})
+ target_link_libraries(solidity PUBLIC CVC4::CVC4)
endif()
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index 9d041ce5..e637795a 100644
--- a/libsolidity/analysis/ConstantEvaluator.cpp
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/ConstantEvaluator.h>
+
#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
@@ -41,7 +42,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
auto right = type(_operation.rightExpression());
if (left && right)
{
- auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
+ TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
if (!commonType)
m_errorReporter.fatalTypeError(
_operation.location(),
diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp
index 6dc564de..96b9611e 100644
--- a/libsolidity/analysis/ContractLevelChecker.cpp
+++ b/libsolidity/analysis/ContractLevelChecker.cpp
@@ -20,10 +20,9 @@
*/
#include <libsolidity/analysis/ContractLevelChecker.h>
-#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
-
#include <boost/range/adaptor/reversed.hpp>
@@ -227,7 +226,7 @@ void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _con
return _type->hasEqualParameterTypes(*_funAndFlag.first);
});
if (it == overloads.end())
- overloads.push_back(make_pair(_type, _implemented));
+ overloads.emplace_back(_type, _implemented);
else if (it->second)
{
if (!_implemented)
@@ -409,8 +408,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
auto functionType = make_shared<FunctionType>(*f);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
- externalDeclarations[functionType->externalSignature()].push_back(
- make_pair(f, functionType->asCallableFunction(false))
+ externalDeclarations[functionType->externalSignature()].emplace_back(
+ f, functionType->asCallableFunction(false)
);
}
for (VariableDeclaration const* v: contract->stateVariables())
@@ -419,8 +418,8 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
auto functionType = make_shared<FunctionType>(*v);
// under non error circumstances this should be true
if (functionType->interfaceFunctionType())
- externalDeclarations[functionType->externalSignature()].push_back(
- make_pair(v, functionType->asCallableFunction(false))
+ externalDeclarations[functionType->externalSignature()].emplace_back(
+ v, functionType->asCallableFunction(false)
);
}
}
diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h
index 15cbf45d..d754687a 100644
--- a/libsolidity/analysis/ContractLevelChecker.h
+++ b/libsolidity/analysis/ContractLevelChecker.h
@@ -22,7 +22,6 @@
#pragma once
#include <libsolidity/ast/ASTForward.h>
-
#include <map>
namespace langutil
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp
index fe58f0aa..3adf6318 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.cpp
+++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp
@@ -16,7 +16,9 @@
*/
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
+
#include <liblangutil/SourceLocation.h>
+#include <boost/range/algorithm/sort.hpp>
using namespace std;
using namespace langutil;
@@ -33,131 +35,112 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function)
if (_function.isImplemented())
{
auto const& functionFlow = m_cfg.functionFlow(_function);
- checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit);
+ checkUninitializedAccess(functionFlow.entry, functionFlow.exit);
}
return false;
}
-set<VariableDeclaration const*> ControlFlowAnalyzer::variablesAssignedInNode(CFGNode const *node)
+void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const
{
- set<VariableDeclaration const*> result;
- for (auto expression: node->block.expressions)
+ struct NodeInfo
{
- if (auto const* assignment = dynamic_cast<Assignment const*>(expression))
+ set<VariableDeclaration const*> unassignedVariablesAtEntry;
+ set<VariableDeclaration const*> unassignedVariablesAtExit;
+ set<VariableOccurrence const*> uninitializedVariableAccesses;
+ /// Propagate the information from another node to this node.
+ /// To be used to propagate information from a node to its exit nodes.
+ /// Returns true, if new variables were added and thus the current node has
+ /// to be traversed again.
+ bool propagateFrom(NodeInfo const& _entryNode)
{
- stack<Expression const*> expressions;
- expressions.push(&assignment->leftHandSide());
- while (!expressions.empty())
- {
- Expression const* expression = expressions.top();
- expressions.pop();
-
- if (auto const *tuple = dynamic_cast<TupleExpression const*>(expression))
- for (auto const& component: tuple->components())
- expressions.push(component.get());
- else if (auto const* identifier = dynamic_cast<Identifier const*>(expression))
- if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(
- identifier->annotation().referencedDeclaration
- ))
- result.insert(variableDeclaration);
- }
+ size_t previousUnassignedVariablesAtEntry = unassignedVariablesAtEntry.size();
+ size_t previousUninitializedVariableAccessess = uninitializedVariableAccesses.size();
+ unassignedVariablesAtEntry += _entryNode.unassignedVariablesAtExit;
+ uninitializedVariableAccesses += _entryNode.uninitializedVariableAccesses;
+ return
+ unassignedVariablesAtEntry.size() > previousUnassignedVariablesAtEntry ||
+ uninitializedVariableAccesses.size() > previousUninitializedVariableAccessess
+ ;
}
- }
- return result;
-}
-
-void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
- FunctionDefinition const& _function,
- CFGNode const* _functionEntry,
- CFGNode const* _functionExit
-) const
-{
- if (_function.returnParameterList()->parameters().empty())
- return;
-
- map<CFGNode const*, set<VariableDeclaration const*>> unassigned;
-
- {
- auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
- for (auto const& returnParameter: _function.returnParameterList()->parameters())
- if (
- returnParameter->type()->dataStoredIn(DataLocation::Storage) ||
- returnParameter->type()->category() == Type::Category::Mapping
- )
- unassignedAtFunctionEntry.insert(returnParameter.get());
- }
-
- stack<CFGNode const*> nodesToTraverse;
- nodesToTraverse.push(_functionEntry);
-
- // walk all paths from entry with maximal set of unassigned return values
+ };
+ map<CFGNode const*, NodeInfo> nodeInfos;
+ set<CFGNode const*> nodesToTraverse;
+ nodesToTraverse.insert(_entry);
+
+ // Walk all paths starting from the nodes in ``nodesToTraverse`` until ``NodeInfo::propagateFrom``
+ // returns false for all exits, i.e. until all paths have been walked with maximal sets of unassigned
+ // variables and accesses.
while (!nodesToTraverse.empty())
{
- auto node = nodesToTraverse.top();
- nodesToTraverse.pop();
-
- auto& unassignedAtNode = unassigned[node];
+ CFGNode const* currentNode = *nodesToTraverse.begin();
+ nodesToTraverse.erase(nodesToTraverse.begin());
- if (node->block.returnStatement != nullptr)
- if (node->block.returnStatement->expression())
- unassignedAtNode.clear();
- if (!unassignedAtNode.empty())
+ auto& nodeInfo = nodeInfos[currentNode];
+ auto unassignedVariables = nodeInfo.unassignedVariablesAtEntry;
+ for (auto const& variableOccurrence: currentNode->variableOccurrences)
{
- // kill all return values to which a value is assigned
- for (auto const* variableDeclaration: variablesAssignedInNode(node))
- unassignedAtNode.erase(variableDeclaration);
-
- // kill all return values referenced in inline assembly
- // a reference is enough, checking whether there actually was an assignment might be overkill
- for (auto assembly: node->block.inlineAssemblyStatements)
- for (auto const& ref: assembly->annotation().externalReferences)
- if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
- unassignedAtNode.erase(variableDeclaration);
+ switch (variableOccurrence.kind())
+ {
+ case VariableOccurrence::Kind::Assignment:
+ unassignedVariables.erase(&variableOccurrence.declaration());
+ break;
+ case VariableOccurrence::Kind::InlineAssembly:
+ // We consider all variables referenced in inline assembly as accessed.
+ // So far any reference is enough, but we might want to actually analyze
+ // the control flow in the assembly at some point.
+ case VariableOccurrence::Kind::Access:
+ case VariableOccurrence::Kind::Return:
+ if (unassignedVariables.count(&variableOccurrence.declaration()))
+ {
+ if (variableOccurrence.declaration().type()->dataStoredIn(DataLocation::Storage))
+ // Merely store the unassigned access. We do not generate an error right away, since this
+ // path might still always revert. It is only an error if this is propagated to the exit
+ // node of the function (i.e. there is a path with an uninitialized access).
+ nodeInfo.uninitializedVariableAccesses.insert(&variableOccurrence);
+ }
+ break;
+ case VariableOccurrence::Kind::Declaration:
+ unassignedVariables.insert(&variableOccurrence.declaration());
+ break;
+ }
}
+ nodeInfo.unassignedVariablesAtExit = std::move(unassignedVariables);
- for (auto const& exit: node->exits)
- {
- auto& unassignedAtExit = unassigned[exit];
- auto oldSize = unassignedAtExit.size();
- unassignedAtExit.insert(unassignedAtNode.begin(), unassignedAtNode.end());
- // (re)traverse an exit, if we are on a path with new unassigned return values to consider
- // this will terminate, since there is only a finite number of unassigned return values
- if (unassignedAtExit.size() > oldSize)
- nodesToTraverse.push(exit);
- }
+ // Propagate changes to all exits and queue them for traversal, if needed.
+ for (auto const& exit: currentNode->exits)
+ if (nodeInfos[exit].propagateFrom(nodeInfo))
+ nodesToTraverse.insert(exit);
}
- if (!unassigned[_functionExit].empty())
+ auto const& exitInfo = nodeInfos[_exit];
+ if (!exitInfo.uninitializedVariableAccesses.empty())
{
- vector<VariableDeclaration const*> unassignedOrdered(
- unassigned[_functionExit].begin(),
- unassigned[_functionExit].end()
- );
- sort(
- unassignedOrdered.begin(),
- unassignedOrdered.end(),
- [](VariableDeclaration const* lhs, VariableDeclaration const* rhs) -> bool {
- return lhs->id() < rhs->id();
+ vector<VariableOccurrence const*> uninitializedAccessesOrdered(
+ exitInfo.uninitializedVariableAccesses.begin(),
+ exitInfo.uninitializedVariableAccesses.end()
+ );
+ boost::range::sort(
+ uninitializedAccessesOrdered,
+ [](VariableOccurrence const* lhs, VariableOccurrence const* rhs) -> bool
+ {
+ return *lhs < *rhs;
}
);
- for (auto const* returnVal: unassignedOrdered)
+
+ for (auto const* variableOccurrence: uninitializedAccessesOrdered)
{
SecondarySourceLocation ssl;
- for (CFGNode* lastNodeBeforeExit: _functionExit->entries)
- if (unassigned[lastNodeBeforeExit].count(returnVal))
- {
- if (!!lastNodeBeforeExit->block.returnStatement)
- ssl.append("Problematic return:", lastNodeBeforeExit->block.returnStatement->location());
- else
- ssl.append("Problematic end of function:", _function.location());
- }
+ if (variableOccurrence->occurrence())
+ ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
m_errorReporter.typeError(
- returnVal->location(),
+ variableOccurrence->occurrence() ?
+ variableOccurrence->occurrence()->location() :
+ variableOccurrence->declaration().location(),
ssl,
- "This variable is of storage pointer type and might be returned without assignment and "
- "could be used uninitialized. Assign the variable (potentially from itself) "
- "to fix this error."
+ string("This variable is of storage pointer type and can be ") +
+ (variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") +
+ " without prior assignment."
);
}
}
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h
index 411d57ff..7761817a 100644
--- a/libsolidity/analysis/ControlFlowAnalyzer.h
+++ b/libsolidity/analysis/ControlFlowAnalyzer.h
@@ -18,7 +18,6 @@
#pragma once
#include <libsolidity/analysis/ControlFlowGraph.h>
-
#include <set>
namespace dev
@@ -37,12 +36,8 @@ public:
bool visit(FunctionDefinition const& _function) override;
private:
- static std::set<VariableDeclaration const*> variablesAssignedInNode(CFGNode const *node);
- void checkUnassignedStorageReturnValues(
- FunctionDefinition const& _function,
- CFGNode const* _functionEntry,
- CFGNode const* _functionExit
- ) const;
+ /// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit.
+ void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const;
CFG const& m_cfg;
langutil::ErrorReporter& m_errorReporter;
diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp
index 5bd39da3..3dab8b16 100644
--- a/libsolidity/analysis/ControlFlowBuilder.cpp
+++ b/libsolidity/analysis/ControlFlowBuilder.cpp
@@ -22,7 +22,10 @@ using namespace solidity;
using namespace std;
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
- m_nodeContainer(_nodeContainer), m_currentFunctionFlow(_functionFlow), m_currentNode(_functionFlow.entry)
+ m_nodeContainer(_nodeContainer),
+ m_currentNode(_functionFlow.entry),
+ m_returnNode(_functionFlow.exit),
+ m_revertNode(_functionFlow.revert)
{
}
@@ -37,26 +40,8 @@ unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
functionFlow->revert = _nodeContainer.newNode();
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
builder.appendControlFlow(_function);
- connect(builder.m_currentNode, functionFlow->exit);
- return functionFlow;
-}
-
-unique_ptr<ModifierFlow> ControlFlowBuilder::createModifierFlow(
- CFG::NodeContainer& _nodeContainer,
- ModifierDefinition const& _modifier
-)
-{
- auto modifierFlow = unique_ptr<ModifierFlow>(new ModifierFlow());
- modifierFlow->entry = _nodeContainer.newNode();
- modifierFlow->exit = _nodeContainer.newNode();
- modifierFlow->revert = _nodeContainer.newNode();
- modifierFlow->placeholderEntry = _nodeContainer.newNode();
- modifierFlow->placeholderExit = _nodeContainer.newNode();
- ControlFlowBuilder builder(_nodeContainer, *modifierFlow);
- builder.appendControlFlow(_modifier);
- connect(builder.m_currentNode, modifierFlow->exit);
- return modifierFlow;
+ return functionFlow;
}
bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
@@ -219,64 +204,24 @@ bool ControlFlowBuilder::visit(Continue const&)
bool ControlFlowBuilder::visit(Throw const&)
{
solAssert(!!m_currentNode, "");
- solAssert(!!m_currentFunctionFlow.revert, "");
- connect(m_currentNode, m_currentFunctionFlow.revert);
+ solAssert(!!m_revertNode, "");
+ connect(m_currentNode, m_revertNode);
m_currentNode = newLabel();
return false;
}
-bool ControlFlowBuilder::visit(Block const&)
-{
- solAssert(!!m_currentNode, "");
- createLabelHere();
- return true;
-}
-
-void ControlFlowBuilder::endVisit(Block const&)
-{
- solAssert(!!m_currentNode, "");
- createLabelHere();
-}
-
-bool ControlFlowBuilder::visit(Return const& _return)
-{
- solAssert(!!m_currentNode, "");
- solAssert(!!m_currentFunctionFlow.exit, "");
- solAssert(!m_currentNode->block.returnStatement, "");
- m_currentNode->block.returnStatement = &_return;
- connect(m_currentNode, m_currentFunctionFlow.exit);
- m_currentNode = newLabel();
- return true;
-}
-
-
bool ControlFlowBuilder::visit(PlaceholderStatement const&)
{
solAssert(!!m_currentNode, "");
- auto modifierFlow = dynamic_cast<ModifierFlow const*>(&m_currentFunctionFlow);
- solAssert(!!modifierFlow, "");
-
- connect(m_currentNode, modifierFlow->placeholderEntry);
+ solAssert(!!m_placeholderEntry, "");
+ solAssert(!!m_placeholderExit, "");
+ connect(m_currentNode, m_placeholderEntry);
m_currentNode = newLabel();
-
- connect(modifierFlow->placeholderExit, m_currentNode);
+ connect(m_placeholderExit, m_currentNode);
return false;
}
-bool ControlFlowBuilder::visitNode(ASTNode const& node)
-{
- solAssert(!!m_currentNode, "");
- if (auto const* expression = dynamic_cast<Expression const*>(&node))
- m_currentNode->block.expressions.emplace_back(expression);
- else if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(&node))
- m_currentNode->block.variableDeclarations.emplace_back(variableDeclaration);
- else if (auto const* assembly = dynamic_cast<InlineAssembly const*>(&node))
- m_currentNode->block.inlineAssemblyStatements.emplace_back(assembly);
-
- return true;
-}
-
bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
{
solAssert(!!m_currentNode, "");
@@ -286,19 +231,19 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
switch (functionType->kind())
{
case FunctionType::Kind::Revert:
- solAssert(!!m_currentFunctionFlow.revert, "");
+ solAssert(!!m_revertNode, "");
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
- connect(m_currentNode, m_currentFunctionFlow.revert);
+ connect(m_currentNode, m_revertNode);
m_currentNode = newLabel();
return false;
case FunctionType::Kind::Require:
case FunctionType::Kind::Assert:
{
- solAssert(!!m_currentFunctionFlow.revert, "");
+ solAssert(!!m_revertNode, "");
_functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this);
- connect(m_currentNode, m_currentFunctionFlow.revert);
+ connect(m_currentNode, m_revertNode);
auto nextNode = newLabel();
connect(m_currentNode, nextNode);
m_currentNode = nextNode;
@@ -310,6 +255,183 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
return ASTConstVisitor::visit(_functionCall);
}
+bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
+{
+ if (auto arguments = _modifierInvocation.arguments())
+ for (auto& argument: *arguments)
+ appendControlFlow(*argument);
+
+ auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
+ _modifierInvocation.name()->annotation().referencedDeclaration
+ );
+ if (!modifierDefinition) return false;
+ solAssert(!!modifierDefinition, "");
+ solAssert(!!m_returnNode, "");
+
+ m_placeholderEntry = newLabel();
+ m_placeholderExit = newLabel();
+
+ appendControlFlow(*modifierDefinition);
+ connect(m_currentNode, m_returnNode);
+
+ m_currentNode = m_placeholderEntry;
+ m_returnNode = m_placeholderExit;
+
+ m_placeholderEntry = nullptr;
+ m_placeholderExit = nullptr;
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
+{
+ for (auto const& parameter: _functionDefinition.parameters())
+ appendControlFlow(*parameter);
+
+ for (auto const& returnParameter: _functionDefinition.returnParameters())
+ {
+ appendControlFlow(*returnParameter);
+ m_returnNode->variableOccurrences.emplace_back(
+ *returnParameter,
+ VariableOccurrence::Kind::Return,
+ nullptr
+ );
+
+ }
+
+ for (auto const& modifier: _functionDefinition.modifiers())
+ appendControlFlow(*modifier);
+
+ appendControlFlow(_functionDefinition.body());
+
+ connect(m_currentNode, m_returnNode);
+ m_currentNode = nullptr;
+
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Return const& _return)
+{
+ solAssert(!!m_currentNode, "");
+ solAssert(!!m_returnNode, "");
+ if (_return.expression())
+ {
+ appendControlFlow(*_return.expression());
+ // Returns with return expression are considered to be assignments to the return parameters.
+ for (auto returnParameter: _return.annotation().functionReturnParameters->parameters())
+ m_currentNode->variableOccurrences.emplace_back(
+ *returnParameter,
+ VariableOccurrence::Kind::Assignment,
+ &_return
+ );
+ }
+ connect(m_currentNode, m_returnNode);
+ m_currentNode = newLabel();
+ return true;
+}
+
+bool ControlFlowBuilder::visit(FunctionTypeName const&)
+{
+ // Do not visit the parameters and return values of a function type name.
+ // We do not want to consider them as variable declarations for the control flow graph.
+ return false;
+}
+
+bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly)
+{
+ solAssert(!!m_currentNode, "");
+ for (auto const& ref: _inlineAssembly.annotation().externalReferences)
+ {
+ if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(ref.second.declaration))
+ m_currentNode->variableOccurrences.emplace_back(
+ *variableDeclaration,
+ VariableOccurrence::Kind::InlineAssembly,
+ &_inlineAssembly
+ );
+ }
+ return true;
+}
+
+bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
+{
+ solAssert(!!m_currentNode, "");
+
+ m_currentNode->variableOccurrences.emplace_back(
+ _variableDeclaration,
+ VariableOccurrence::Kind::Declaration,
+ nullptr
+ );
+
+ // Handle declaration with immediate assignment.
+ if (_variableDeclaration.value())
+ m_currentNode->variableOccurrences.emplace_back(
+ _variableDeclaration,
+ VariableOccurrence::Kind::Assignment,
+ _variableDeclaration.value().get()
+ );
+ // Function arguments are considered to be immediately assigned as well (they are "externally assigned").
+ else if (_variableDeclaration.isCallableParameter() && !_variableDeclaration.isReturnParameter())
+ m_currentNode->variableOccurrences.emplace_back(
+ _variableDeclaration,
+ VariableOccurrence::Kind::Assignment,
+ nullptr
+ );
+ return true;
+}
+
+bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
+{
+ solAssert(!!m_currentNode, "");
+
+ for (auto const& var: _variableDeclarationStatement.declarations())
+ if (var)
+ var->accept(*this);
+ if (_variableDeclarationStatement.initialValue())
+ {
+ _variableDeclarationStatement.initialValue()->accept(*this);
+ for (size_t i = 0; i < _variableDeclarationStatement.declarations().size(); i++)
+ if (auto const& var = _variableDeclarationStatement.declarations()[i])
+ {
+ auto expression = _variableDeclarationStatement.initialValue();
+ if (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
+ if (tupleExpression->components().size() > 1)
+ {
+ solAssert(tupleExpression->components().size() > i, "");
+ expression = tupleExpression->components()[i].get();
+ }
+ while (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
+ if (tupleExpression->components().size() == 1)
+ expression = tupleExpression->components().front().get();
+ else
+ break;
+ m_currentNode->variableOccurrences.emplace_back(
+ *var,
+ VariableOccurrence::Kind::Assignment,
+ expression
+ );
+ }
+ }
+ return false;
+}
+
+bool ControlFlowBuilder::visit(Identifier const& _identifier)
+{
+ solAssert(!!m_currentNode, "");
+
+ if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
+ m_currentNode->variableOccurrences.emplace_back(
+ *variableDeclaration,
+ static_cast<Expression const&>(_identifier).annotation().lValueRequested ?
+ VariableOccurrence::Kind::Assignment :
+ VariableOccurrence::Kind::Access,
+ &_identifier
+ );
+
+ return true;
+}
+
+
+
void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
{
_node.accept(*this);
diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h
index 40605e00..f196e5fc 100644
--- a/libsolidity/analysis/ControlFlowBuilder.h
+++ b/libsolidity/analysis/ControlFlowBuilder.h
@@ -38,14 +38,11 @@ public:
CFG::NodeContainer& _nodeContainer,
FunctionDefinition const& _function
);
- static std::unique_ptr<ModifierFlow> createModifierFlow(
- CFG::NodeContainer& _nodeContainer,
- ModifierDefinition const& _modifier
- );
private:
explicit ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow);
+ // Visits for constructing the control flow.
bool visit(BinaryOperation const& _operation) override;
bool visit(Conditional const& _conditional) override;
bool visit(IfStatement const& _ifStatement) override;
@@ -54,12 +51,20 @@ private:
bool visit(Break const&) override;
bool visit(Continue const&) override;
bool visit(Throw const&) override;
- bool visit(Block const&) override;
- void endVisit(Block const&) override;
- bool visit(Return const& _return) override;
bool visit(PlaceholderStatement const&) override;
bool visit(FunctionCall const& _functionCall) override;
+ bool visit(ModifierInvocation const& _modifierInvocation) override;
+
+ // Visits for constructing the control flow as well as filling variable occurrences.
+ bool visit(FunctionDefinition const& _functionDefinition) override;
+ bool visit(Return const& _return) override;
+ // Visits for filling variable occurrences.
+ bool visit(FunctionTypeName const& _functionTypeName) override;
+ bool visit(InlineAssembly const& _inlineAssembly) override;
+ bool visit(VariableDeclaration const& _variableDeclaration) override;
+ bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
+ bool visit(Identifier const& _identifier) override;
/// Appends the control flow of @a _node to the current control flow.
void appendControlFlow(ASTNode const& _node);
@@ -73,9 +78,6 @@ private:
static void connect(CFGNode* _from, CFGNode* _to);
-protected:
- bool visitNode(ASTNode const& node) override;
-
private:
/// Splits the control flow starting at the current node into n paths.
@@ -114,17 +116,18 @@ private:
CFG::NodeContainer& m_nodeContainer;
- /// The control flow of the function that is currently parsed.
- /// Note: this can also be a ModifierFlow
- FunctionFlow const& m_currentFunctionFlow;
-
CFGNode* m_currentNode = nullptr;
+ CFGNode* m_returnNode = nullptr;
+ CFGNode* m_revertNode = nullptr;
/// The current jump destination of break Statements.
CFGNode* m_breakJump = nullptr;
/// The current jump destination of continue Statements.
CFGNode* m_continueJump = nullptr;
+ CFGNode* m_placeholderEntry = nullptr;
+ CFGNode* m_placeholderExit = nullptr;
+
/// Helper class that replaces the break and continue jump destinations for the
/// current scope and restores the originals at the end of the scope.
class BreakContinueScope
diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp
index b8860158..8960166a 100644
--- a/libsolidity/analysis/ControlFlowGraph.cpp
+++ b/libsolidity/analysis/ControlFlowGraph.cpp
@@ -16,10 +16,9 @@
*/
#include <libsolidity/analysis/ControlFlowGraph.h>
-#include <libsolidity/analysis/ControlFlowBuilder.h>
+#include <libsolidity/analysis/ControlFlowBuilder.h>
#include <boost/range/adaptor/reversed.hpp>
-
#include <algorithm>
using namespace std;
@@ -29,20 +28,14 @@ using namespace dev::solidity;
bool CFG::constructFlow(ASTNode const& _astRoot)
{
_astRoot.accept(*this);
- applyModifiers();
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
-bool CFG::visit(ModifierDefinition const& _modifier)
-{
- m_modifierControlFlow[&_modifier] = ControlFlowBuilder::createModifierFlow(m_nodeContainer, _modifier);
- return false;
-}
-
bool CFG::visit(FunctionDefinition const& _function)
{
- m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
+ if (_function.isImplemented())
+ m_functionControlFlow[&_function] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function);
return false;
}
@@ -57,81 +50,3 @@ CFGNode* CFG::NodeContainer::newNode()
m_nodes.emplace_back(new CFGNode());
return m_nodes.back().get();
}
-
-void CFG::applyModifiers()
-{
- for (auto const& function: m_functionControlFlow)
- {
- for (auto const& modifierInvocation: boost::adaptors::reverse(function.first->modifiers()))
- {
- if (auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
- modifierInvocation->name()->annotation().referencedDeclaration
- ))
- {
- solAssert(m_modifierControlFlow.count(modifierDefinition), "");
- applyModifierFlowToFunctionFlow(*m_modifierControlFlow[modifierDefinition], function.second.get());
- }
- }
- }
-}
-
-void CFG::applyModifierFlowToFunctionFlow(
- ModifierFlow const& _modifierFlow,
- FunctionFlow* _functionFlow
-)
-{
- solAssert(!!_functionFlow, "");
-
- map<CFGNode*, CFGNode*> copySrcToCopyDst;
-
- // inherit the revert node of the function
- copySrcToCopyDst[_modifierFlow.revert] = _functionFlow->revert;
-
- // replace the placeholder nodes by the function entry and exit
- copySrcToCopyDst[_modifierFlow.placeholderEntry] = _functionFlow->entry;
- copySrcToCopyDst[_modifierFlow.placeholderExit] = _functionFlow->exit;
-
- stack<CFGNode*> nodesToCopy;
- nodesToCopy.push(_modifierFlow.entry);
-
- // map the modifier entry to a new node that will become the new function entry
- copySrcToCopyDst[_modifierFlow.entry] = m_nodeContainer.newNode();
-
- while (!nodesToCopy.empty())
- {
- CFGNode* copySrcNode = nodesToCopy.top();
- nodesToCopy.pop();
-
- solAssert(copySrcToCopyDst.count(copySrcNode), "");
-
- CFGNode* copyDstNode = copySrcToCopyDst[copySrcNode];
-
- copyDstNode->block = copySrcNode->block;
- for (auto const& entry: copySrcNode->entries)
- {
- if (!copySrcToCopyDst.count(entry))
- {
- copySrcToCopyDst[entry] = m_nodeContainer.newNode();
- nodesToCopy.push(entry);
- }
- copyDstNode->entries.emplace_back(copySrcToCopyDst[entry]);
- }
- for (auto const& exit: copySrcNode->exits)
- {
- if (!copySrcToCopyDst.count(exit))
- {
- copySrcToCopyDst[exit] = m_nodeContainer.newNode();
- nodesToCopy.push(exit);
- }
- copyDstNode->exits.emplace_back(copySrcToCopyDst[exit]);
- }
- }
-
- // if the modifier control flow never reached its exit node,
- // we need to create a new (disconnected) exit node now
- if (!copySrcToCopyDst.count(_modifierFlow.exit))
- copySrcToCopyDst[_modifierFlow.exit] = m_nodeContainer.newNode();
-
- _functionFlow->entry = copySrcToCopyDst[_modifierFlow.entry];
- _functionFlow->exit = copySrcToCopyDst[_modifierFlow.exit];
-}
diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h
index 8fe9fe8e..cc0113d8 100644
--- a/libsolidity/analysis/ControlFlowGraph.h
+++ b/libsolidity/analysis/ControlFlowGraph.h
@@ -31,25 +31,57 @@ namespace dev
namespace solidity
{
-/** Basic Control Flow Block.
- * Basic block of control flow. Consists of a set of (unordered) AST nodes
- * for which control flow is always linear. A basic control flow block
- * encompasses at most one scope. Reverts are considered to break the control
- * flow.
- * @todo Handle function calls correctly. So far function calls are not considered
- * to change the control flow.
- */
-struct ControlFlowBlock
+/** Occurrence of a variable in a block of control flow.
+ * Stores the declaration of the referenced variable, the
+ * kind of the occurrence and possibly the node at which
+ * it occurred.
+ */
+class VariableOccurrence
{
- /// All variable declarations inside this control flow block.
- std::vector<VariableDeclaration const*> variableDeclarations;
- /// All expressions inside this control flow block (this includes all subexpressions!).
- std::vector<Expression const*> expressions;
- /// All inline assembly statements inside in this control flow block.
- std::vector<InlineAssembly const*> inlineAssemblyStatements;
- /// If control flow returns in this node, the return statement is stored in returnStatement,
- /// otherwise returnStatement is nullptr.
- Return const* returnStatement = nullptr;
+public:
+ enum class Kind
+ {
+ Declaration,
+ Access,
+ Return,
+ Assignment,
+ InlineAssembly
+ };
+ VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, ASTNode const* _occurrence):
+ m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence)
+ {
+ }
+
+ /// Defines a deterministic order on variable occurrences.
+ bool operator<(VariableOccurrence const& _rhs) const
+ {
+ if (m_occurrence && _rhs.m_occurrence)
+ {
+ if (m_occurrence->id() < _rhs.m_occurrence->id()) return true;
+ if (_rhs.m_occurrence->id() < m_occurrence->id()) return false;
+ }
+ else if (_rhs.m_occurrence)
+ return true;
+ else if (m_occurrence)
+ return false;
+
+ using KindCompareType = std::underlying_type<VariableOccurrence::Kind>::type;
+ return
+ std::make_pair(m_declaration.id(), static_cast<KindCompareType>(m_occurrenceKind)) <
+ std::make_pair(_rhs.m_declaration.id(), static_cast<KindCompareType>(_rhs.m_occurrenceKind))
+ ;
+ }
+
+ VariableDeclaration const& declaration() const { return m_declaration; }
+ Kind kind() const { return m_occurrenceKind; };
+ ASTNode const* occurrence() const { return m_occurrence; }
+private:
+ /// Declaration of the occurring variable.
+ VariableDeclaration const& m_declaration;
+ /// Kind of occurrence.
+ Kind m_occurrenceKind = Kind::Access;
+ /// AST node at which the variable occurred, if available (may be nullptr).
+ ASTNode const* m_occurrence = nullptr;
};
/** Node of the Control Flow Graph.
@@ -64,14 +96,15 @@ struct CFGNode
/// Exit nodes. All CFG nodes to which control flow may continue after this node.
std::vector<CFGNode*> exits;
- /// Control flow in the node.
- ControlFlowBlock block;
+ /// Variable occurrences in the node.
+ std::vector<VariableOccurrence> variableOccurrences;
};
/** Describes the control flow of a function. */
struct FunctionFlow
{
- virtual ~FunctionFlow() {}
+ virtual ~FunctionFlow() = default;
+
/// Entry node. Control flow of the function starts here.
/// This node is empty and does not have any entries.
CFGNode* entry = nullptr;
@@ -85,19 +118,6 @@ struct FunctionFlow
CFGNode* revert = nullptr;
};
-/** Describes the control flow of a modifier.
- * Every placeholder breaks the control flow. The node preceding the
- * placeholder is assigned placeholderEntry as exit and the node
- * following the placeholder is assigned placeholderExit as entry.
- */
-struct ModifierFlow: FunctionFlow
-{
- /// Control flow leading towards a placeholder exit in placeholderEntry.
- CFGNode* placeholderEntry = nullptr;
- /// Control flow coming from a placeholder enter from placeholderExit.
- CFGNode* placeholderExit = nullptr;
-};
-
class CFG: private ASTConstVisitor
{
public:
@@ -105,7 +125,6 @@ public:
bool constructFlow(ASTNode const& _astRoot);
- bool visit(ModifierDefinition const& _modifier) override;
bool visit(FunctionDefinition const& _function) override;
FunctionFlow const& functionFlow(FunctionDefinition const& _function) const;
@@ -118,20 +137,6 @@ public:
std::vector<std::unique_ptr<CFGNode>> m_nodes;
};
private:
- /// Initially the control flow for all functions *ignoring* modifiers and for
- /// all modifiers is constructed. Afterwards the control flow of functions
- /// is adjusted by applying all modifiers.
- void applyModifiers();
-
- /// Creates a copy of the modifier flow @a _modifierFlow, while replacing the
- /// placeholder entry and exit with the function entry and exit, as well as
- /// replacing the modifier revert node with the function's revert node.
- /// The resulting control flow is the new function flow with the modifier applied.
- /// @a _functionFlow is updated in-place.
- void applyModifierFlowToFunctionFlow(
- ModifierFlow const& _modifierFlow,
- FunctionFlow* _functionFlow
- );
langutil::ErrorReporter& m_errorReporter;
@@ -141,7 +146,6 @@ private:
NodeContainer m_nodeContainer;
std::map<FunctionDefinition const*, std::unique_ptr<FunctionFlow>> m_functionControlFlow;
- std::map<ModifierDefinition const*, std::unique_ptr<ModifierFlow>> m_modifierControlFlow;
};
}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index cf12a49d..d0657898 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/DeclarationContainer.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
#include <libdevcore/StringUtils.h>
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 9d7a17a3..e26f5891 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -22,11 +22,10 @@
#pragma once
+#include <libsolidity/ast/ASTForward.h>
+#include <boost/noncopyable.hpp>
#include <map>
#include <set>
-#include <boost/noncopyable.hpp>
-
-#include <libsolidity/ast/ASTForward.h>
namespace dev
{
diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp
index 69a7a43c..0878b550 100644
--- a/libsolidity/analysis/DocStringAnalyser.cpp
+++ b/libsolidity/analysis/DocStringAnalyser.cpp
@@ -22,9 +22,10 @@
*/
#include <libsolidity/analysis/DocStringAnalyser.h>
+
#include <libsolidity/ast/AST.h>
-#include <liblangutil/ErrorReporter.h>
#include <libsolidity/parsing/DocStringParser.h>
+#include <liblangutil/ErrorReporter.h>
using namespace std;
using namespace dev;
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index cba2655c..cd5fe07d 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -21,10 +21,11 @@
* Container of the (implicit and explicit) global objects.
*/
-#include <memory>
#include <libsolidity/analysis/GlobalContext.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
+#include <memory>
using namespace std;
diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h
index 4ed08711..09611c41 100644
--- a/libsolidity/analysis/GlobalContext.h
+++ b/libsolidity/analysis/GlobalContext.h
@@ -22,12 +22,12 @@
#pragma once
-#include <string>
-#include <vector>
+#include <libsolidity/ast/ASTForward.h>
+#include <boost/noncopyable.hpp>
#include <map>
#include <memory>
-#include <boost/noncopyable.hpp>
-#include <libsolidity/ast/ASTForward.h>
+#include <string>
+#include <vector>
namespace dev
{
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 0528a200..95bc69fe 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -22,11 +22,10 @@
#include <libsolidity/analysis/NameAndTypeResolver.h>
-#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/ast/AST.h>
#include <liblangutil/ErrorReporter.h>
#include <libdevcore/StringUtils.h>
-
#include <boost/algorithm/string.hpp>
using namespace std;
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 1b034ef4..89c53932 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -22,13 +22,15 @@
#pragma once
-#include <map>
-#include <list>
-#include <boost/noncopyable.hpp>
#include <libsolidity/analysis/DeclarationContainer.h>
#include <libsolidity/analysis/ReferencesResolver.h>
-#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <list>
+#include <map>
namespace langutil
{
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index 27cbcd45..6a7e5c7e 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -16,15 +16,14 @@
*/
#include <libsolidity/analysis/PostTypeChecker.h>
-#include <libsolidity/ast/AST.h>
+
#include <libsolidity/analysis/SemVerHandler.h>
-#include <liblangutil/ErrorReporter.h>
+#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/Version.h>
-
+#include <liblangutil/ErrorReporter.h>
#include <libdevcore/Algorithms.h>
#include <boost/range/adaptor/map.hpp>
-
#include <memory>
using namespace std;
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index c4931d98..4e6d7f59 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -21,12 +21,15 @@
*/
#include <libsolidity/analysis/ReferencesResolver.h>
-#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
+#include <libsolidity/ast/AST.h>
+
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
+#include <libyul/backends/evm/EVMDialect.h>
+
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
@@ -316,7 +319,14 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// We use the latest EVM version because we will re-run it anyway.
yul::AsmAnalysisInfo analysisInfo;
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
- yul::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, yul::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
+ yul::AsmAnalyzer(
+ analysisInfo,
+ errorsIgnored,
+ EVMVersion(),
+ errorTypeForLoose,
+ yul::EVMDialect::looseAssemblyForEVM(),
+ resolver
+ ).analyze(_inlineAssembly.operations());
return false;
}
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
index 32c0553f..b3de9458 100644
--- a/libsolidity/analysis/ReferencesResolver.h
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -22,12 +22,13 @@
#pragma once
-#include <map>
-#include <list>
-#include <boost/noncopyable.hpp>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <boost/noncopyable.hpp>
+#include <list>
+#include <map>
+
namespace langutil
{
class ErrorReporter;
diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp
index 64fa17b3..7c6ba91f 100644
--- a/libsolidity/analysis/SemVerHandler.cpp
+++ b/libsolidity/analysis/SemVerHandler.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/analysis/SemVerHandler.h>
+
#include <functional>
using namespace std;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 38391841..aaaa4f9f 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 <liblangutil/ErrorReporter.h>
@@ -63,21 +64,21 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
- m_currentFunction = nullptr;
- m_constructor = false;
- for (auto const& var: m_localVarUseCount)
- if (var.second == 0)
- {
- if (var.first.second->isCallableParameter())
- m_errorReporter.warning(
- var.first.second->location(),
- "Unused function parameter. Remove or comment out the variable name to silence this warning."
- );
- else
- m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
- }
-
+ if (m_currentFunction && !m_currentFunction->body().statements().empty())
+ for (auto const& var: m_localVarUseCount)
+ if (var.second == 0)
+ {
+ if (var.first.second->isCallableParameter())
+ m_errorReporter.warning(
+ var.first.second->location(),
+ "Unused function parameter. Remove or comment out the variable name to silence this warning."
+ );
+ else
+ m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
+ }
m_localVarUseCount.clear();
+ m_constructor = false;
+ m_currentFunction = nullptr;
}
bool StaticAnalyzer::visit(Identifier const& _identifier)
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index a73d7e5c..066b5004 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -16,15 +16,18 @@
*/
#include <libsolidity/analysis/SyntaxChecker.h>
-#include <memory>
+
+#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ExperimentalFeatures.h>
-#include <libsolidity/analysis/SemVerHandler.h>
-#include <liblangutil/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
-#include <boost/algorithm/cxx11/all_of.hpp>
+#include <liblangutil/ErrorReporter.h>
+
+#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string.hpp>
+
+#include <memory>
#include <string>
using namespace std;
@@ -111,7 +114,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
SemVerMatchExpressionParser parser(tokens, literals);
auto matchExpression = parser.parse();
- SemVerVersion currentVersion{string(VersionString)};
+ static SemVerVersion const currentVersion{string(VersionString)};
if (!matchExpression.matches(currentVersion))
m_errorReporter.syntaxError(
_pragma.location(),
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 9350df05..507a2c94 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -26,6 +26,7 @@
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/ErrorReporter.h>
@@ -33,8 +34,8 @@
#include <libdevcore/StringUtils.h>
#include <boost/algorithm/cxx11/all_of.hpp>
-#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
#include <memory>
#include <vector>
@@ -658,7 +659,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter,
m_evmVersion,
Error::Type::SyntaxError,
- yul::AsmFlavour::Loose,
+ yul::EVMDialect::looseAssemblyForEVM(),
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
@@ -935,30 +936,32 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
var.accept(*this);
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
{
+ auto errorMsg = "Type " +
+ valueComponentType->toString() +
+ " is not implicitly convertible to expected type " +
+ var.annotation().type->toString();
if (
valueComponentType->category() == Type::Category::RationalNumber &&
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
valueComponentType->mobileType()
)
- m_errorReporter.typeError(
- _statement.location(),
- "Type " +
- valueComponentType->toString() +
- " is not implicitly convertible to expected type " +
- var.annotation().type->toString() +
- ". Try converting to type " +
- valueComponentType->mobileType()->toString() +
- " or use an explicit conversion."
- );
+ {
+ if (var.annotation().type->operator==(*valueComponentType->mobileType()))
+ m_errorReporter.typeError(
+ _statement.location(),
+ errorMsg + ", but it can be explicitly converted."
+ );
+ else
+ m_errorReporter.typeError(
+ _statement.location(),
+ errorMsg +
+ ". Try converting to type " +
+ valueComponentType->mobileType()->toString() +
+ " or use an explicit conversion."
+ );
+ }
else
- m_errorReporter.typeError(
- _statement.location(),
- "Type " +
- valueComponentType->toString() +
- " is not implicitly convertible to expected type " +
- var.annotation().type->toString() +
- "."
- );
+ m_errorReporter.typeError(_statement.location(), errorMsg + ".");
}
}
}
@@ -1252,7 +1255,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
{
TypePointer const& leftType = type(_operation.leftExpression());
TypePointer const& rightType = type(_operation.rightExpression());
- TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ TypeResult result = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ TypePointer commonType = result.get();
if (!commonType)
{
m_errorReporter.typeError(
@@ -1262,7 +1266,8 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
" not compatible with types " +
leftType->toString() +
" and " +
- rightType->toString()
+ rightType->toString() +
+ (!result.message().empty() ? ". " + result.message() : "")
);
commonType = leftType;
}
@@ -2329,30 +2334,32 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
_expression.accept(*this);
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
{
+ auto errorMsg = "Type " +
+ type(_expression)->toString() +
+ " is not implicitly convertible to expected type " +
+ _expectedType.toString();
if (
type(_expression)->category() == Type::Category::RationalNumber &&
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
type(_expression)->mobileType()
)
- m_errorReporter.typeError(
- _expression.location(),
- "Type " +
- type(_expression)->toString() +
- " is not implicitly convertible to expected type " +
- _expectedType.toString() +
- ". Try converting to type " +
- type(_expression)->mobileType()->toString() +
- " or use an explicit conversion."
- );
+ {
+ if (_expectedType.operator==(*type(_expression)->mobileType()))
+ m_errorReporter.typeError(
+ _expression.location(),
+ errorMsg + ", but it can be explicitly converted."
+ );
+ else
+ m_errorReporter.typeError(
+ _expression.location(),
+ errorMsg +
+ ". Try converting to type " +
+ type(_expression)->mobileType()->toString() +
+ " or use an explicit conversion."
+ );
+ }
else
- m_errorReporter.typeError(
- _expression.location(),
- "Type " +
- type(_expression)->toString() +
- " is not implicitly convertible to expected type " +
- _expectedType.toString() +
- "."
- );
+ m_errorReporter.typeError(_expression.location(), errorMsg + ".");
return false;
}
return true;
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index ebfcdadc..b60c571a 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -24,10 +24,10 @@
#include <liblangutil/EVMVersion.h>
-#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/ast/Types.h>
namespace langutil
{
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 1112d682..eb019481 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -16,14 +16,10 @@
*/
#include <libsolidity/analysis/ViewPureChecker.h>
-
-#include <libevmasm/SemanticInformation.h>
-
#include <libsolidity/ast/ExperimentalFeatures.h>
#include <libyul/AsmData.h>
-
#include <liblangutil/ErrorReporter.h>
-
+#include <libevmasm/SemanticInformation.h>
#include <functional>
using namespace std;
@@ -156,6 +152,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
_funDef.stateMutability() != StateMutability::Payable &&
_funDef.isImplemented() &&
+ !_funDef.body().statements().empty() &&
!_funDef.isConstructor() &&
!_funDef.isFallback() &&
!_funDef.annotation().superFunction
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 3ae6bd6d..f379d758 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -21,13 +21,12 @@
*/
#include <libsolidity/ast/AST.h>
+
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/AST_accept.h>
-
#include <libdevcore/Keccak256.h>
#include <boost/algorithm/string.hpp>
-
#include <algorithm>
#include <functional>
@@ -198,7 +197,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
{
signaturesSeen.insert(functionSignature);
FixedHash<4> hash(dev::keccak256(functionSignature));
- m_interfaceFunctionList->push_back(make_pair(hash, fun));
+ m_interfaceFunctionList->emplace_back(hash, fun);
}
}
}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 2f418b09..9ac065ea 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -22,24 +22,22 @@
#pragma once
-
-#include <libsolidity/parsing/Token.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTEnums.h>
+#include <libsolidity/parsing/Token.h>
#include <liblangutil/SourceLocation.h>
#include <libevmasm/Instruction.h>
-
#include <libdevcore/FixedHash.h>
-#include <json/json.h>
#include <boost/noncopyable.hpp>
+#include <json/json.h>
+#include <memory>
#include <string>
#include <vector>
-#include <memory>
namespace yul
{
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index e9cc905e..d1acf90b 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -27,8 +27,8 @@
#include <map>
#include <memory>
-#include <vector>
#include <set>
+#include <vector>
namespace yul
{
@@ -46,7 +46,7 @@ using TypePointer = std::shared_ptr<Type const>;
struct ASTAnnotation
{
- virtual ~ASTAnnotation() {}
+ virtual ~ASTAnnotation() = default;
};
struct DocTag
@@ -57,7 +57,7 @@ struct DocTag
struct DocumentedAnnotation
{
- virtual ~DocumentedAnnotation() {}
+ virtual ~DocumentedAnnotation() = default;
/// Mapping docstring tag name -> content.
std::multimap<std::string, DocTag> docTags;
};
diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h
index 992fe4cd..f61acad9 100644
--- a/libsolidity/ast/ASTForward.h
+++ b/libsolidity/ast/ASTForward.h
@@ -22,8 +22,8 @@
#pragma once
-#include <string>
#include <memory>
+#include <string>
#include <vector>
// Forward-declare all AST node types
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index cfb13271..f8222598 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -20,11 +20,12 @@
*/
#include <libsolidity/ast/ASTJsonConverter.h>
-#include <boost/algorithm/string/join.hpp>
-#include <libdevcore/UTF8.h>
+
#include <libsolidity/ast/AST.h>
#include <libyul/AsmData.h>
#include <libyul/AsmPrinter.h>
+#include <libdevcore/UTF8.h>
+#include <boost/algorithm/string/join.hpp>
using namespace std;
using namespace langutil;
@@ -234,7 +235,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)),
make_pair("scope", idOrNull(_node.scope()))
};
- attributes.push_back(make_pair("unitAlias", _node.name()));
+ attributes.emplace_back("unitAlias", _node.name());
Json::Value symbolAliases(Json::arrayValue);
for (auto const& symbolAlias: _node.symbolAliases())
{
@@ -244,7 +245,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
tuple["local"] = symbolAlias.second ? Json::Value(*symbolAlias.second) : Json::nullValue;
symbolAliases.append(tuple);
}
- attributes.push_back(make_pair("symbolAliases", std::move(symbolAliases)));
+ attributes.emplace_back("symbolAliases", std::move(symbolAliases));
setJsonNode(_node, "ImportDirective", std::move(attributes));
return false;
}
@@ -357,7 +358,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
};
if (m_inEvent)
- attributes.push_back(make_pair("indexed", _node.isIndexed()));
+ attributes.emplace_back("indexed", _node.isIndexed());
setJsonNode(_node, "VariableDeclaration", std::move(attributes));
return false;
}
@@ -647,11 +648,11 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
};
if (m_legacy)
{
- attributes.push_back(make_pair("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall));
- attributes.push_back(make_pair("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion));
+ attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall);
+ attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion);
}
else
- attributes.push_back(make_pair("kind", functionCallKind(_node.annotation().kind)));
+ attributes.emplace_back("kind", functionCallKind(_node.annotation().kind));
appendExpressionAttributes(attributes, _node.annotation());
setJsonNode(_node, "FunctionCall", std::move(attributes));
return false;
@@ -724,7 +725,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
std::vector<pair<string, Json::Value>> attributes = {
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
make_pair("value", value),
- make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(_node.value())),
+ make_pair(m_legacy ? "hexvalue" : "hexValue", toHex(asBytes(_node.value()))),
make_pair(
"subdenomination",
subdenomination == Token::Illegal ?
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index ef0a217a..770e3d9d 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -22,12 +22,13 @@
#pragma once
-#include <ostream>
-#include <stack>
+#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/Exceptions.h>
-#include <libsolidity/ast/ASTAnnotations.h>
+
#include <json/json.h>
+#include <ostream>
+#include <stack>
namespace langutil
{
diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
index cdc6ae7d..d8bafa2c 100644
--- a/libsolidity/ast/ASTPrinter.cpp
+++ b/libsolidity/ast/ASTPrinter.cpp
@@ -21,11 +21,10 @@
*/
#include <libsolidity/ast/ASTPrinter.h>
-#include <libsolidity/ast/AST.h>
-
-#include <json/json.h>
+#include <libsolidity/ast/AST.h>
#include <boost/algorithm/string/join.hpp>
+#include <json/json.h>
using namespace std;
using namespace langutil;
diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h
index de3bf8a2..d762af47 100644
--- a/libsolidity/ast/ASTPrinter.h
+++ b/libsolidity/ast/ASTPrinter.h
@@ -22,9 +22,9 @@
#pragma once
-#include <ostream>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/interface/GasEstimator.h>
+#include <ostream>
namespace dev
{
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
index 1a761032..8cb94e05 100644
--- a/libsolidity/ast/ASTVisitor.h
+++ b/libsolidity/ast/ASTVisitor.h
@@ -22,10 +22,10 @@
#pragma once
-#include <string>
+#include <libsolidity/ast/AST.h>
#include <functional>
+#include <string>
#include <vector>
-#include <libsolidity/ast/AST.h>
namespace dev
{
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 6cadb5f3..cc978b4a 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -24,22 +24,22 @@
#include <libsolidity/ast/AST.h>
-#include <libdevcore/CommonIO.h>
+#include <libdevcore/Algorithms.h>
#include <libdevcore/CommonData.h>
+#include <libdevcore/CommonIO.h>
#include <libdevcore/Keccak256.h>
#include <libdevcore/UTF8.h>
-#include <libdevcore/Algorithms.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/join.hpp>
-#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
-#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/range/adaptor/reversed.hpp>
-#include <boost/range/algorithm/copy.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/range/adaptor/transformed.hpp>
-#include <boost/algorithm/string.hpp>
+#include <boost/range/algorithm/copy.hpp>
#include <limits>
@@ -125,6 +125,22 @@ bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2)
return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
}
+/// Checks whether _value fits into IntegerType _type.
+bool fitsIntegerType(bigint const& _value, IntegerType const& _type)
+{
+ return (_type.minValue() <= _value) && (_value <= _type.maxValue());
+}
+
+/// Checks whether _value fits into _bits bits when having 1 bit as the sign bit
+/// if _signed is true.
+bool fitsIntoBits(bigint const& _value, unsigned _bits, bool _signed)
+{
+ return fitsIntegerType(_value, IntegerType(
+ _bits,
+ _signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned
+ ));
+}
+
}
void StorageOffsets::computeOffsets(TypePointers const& _types)
@@ -446,7 +462,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
continue;
FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true);
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
- members.push_back(MemberList::Member(function->name(), fun, function));
+ members.emplace_back(function->name(), fun, function);
}
}
return members;
@@ -466,7 +482,7 @@ string AddressType::richIdentifier() const
return "t_address";
}
-bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
+BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _other) const
{
if (_other.category() != category())
return false;
@@ -475,7 +491,7 @@ bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
return other.m_stateMutability <= m_stateMutability;
}
-bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo))
return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
@@ -504,17 +520,16 @@ u256 AddressType::literalValue(Literal const* _literal) const
return u256(_literal->valueWithoutUnderscores());
}
-TypePointer AddressType::unaryOperatorResult(Token _operator) const
+TypeResult AddressType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
- // Addresses can only be compared.
if (!TokenTraits::isCompareOp(_operator))
- return TypePointer();
+ return TypeResult{"Arithmetic operations on addresses are not supported. Convert to integer first before using them."};
return Type::commonType(shared_from_this(), _other);
}
@@ -576,7 +591,7 @@ string IntegerType::richIdentifier() const
return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
}
-bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() == category())
{
@@ -597,7 +612,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() ||
_convertTo.category() == Category::Address ||
@@ -607,18 +622,17 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == Category::FixedPoint;
}
-TypePointer IntegerType::unaryOperatorResult(Token _operator) const
+TypeResult IntegerType::unaryOperatorResult(Token _operator) const
{
// "delete" is ok for all integer types
if (_operator == Token::Delete)
- return make_shared<TupleType>();
- // we allow +, -, ++ and --
- else if (_operator == Token::Add || _operator == Token::Sub ||
- _operator == Token::Inc || _operator == Token::Dec ||
- _operator == Token::BitNot)
- return shared_from_this();
+ return TypeResult{make_shared<TupleType>()};
+ // we allow -, ++ and --
+ else if (_operator == Token::Sub || _operator == Token::Inc ||
+ _operator == Token::Dec || _operator == Token::BitNot)
+ return TypeResult{shared_from_this()};
else
- return TypePointer();
+ return TypeResult{""};
}
bool IntegerType::operator==(Type const& _other) const
@@ -651,7 +665,7 @@ bigint IntegerType::maxValue() const
return (bigint(1) << m_bits) - 1;
}
-TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (
_other->category() != Category::RationalNumber &&
@@ -679,9 +693,8 @@ TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const
return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
{
- // Signed EXP is not allowed
if (Token::Exp == _operator && intType->isSigned())
- return TypePointer();
+ return TypeResult{"Exponentiation is not allowed for signed integer types."};
}
else if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
if (Token::Exp == _operator)
@@ -704,7 +717,7 @@ string FixedPointType::richIdentifier() const
return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits);
}
-bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() == category())
{
@@ -717,18 +730,18 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
}
-TypePointer FixedPointType::unaryOperatorResult(Token _operator) const
+TypeResult FixedPointType::unaryOperatorResult(Token _operator) const
{
switch(_operator)
{
case Token::Delete:
// "delete" is ok for all fixed types
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
case Token::Add:
case Token::Sub:
case Token::Inc:
@@ -771,7 +784,7 @@ bigint FixedPointType::minIntegerValue() const
return bigint(0);
}
-TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
auto commonType = Type::commonType(shared_from_this(), _other);
@@ -957,7 +970,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
return make_tuple(true, value);
}
-bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
switch (_convertTo.category())
{
@@ -966,27 +979,21 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (isFractional())
return false;
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
- if (m_value == rational(0))
- return true;
- unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
- if (m_value > rational(0))
- {
- if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit)))
- return true;
- return false;
- }
- if (targetType.isSigned())
- {
- if (-m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit)))
- return true;
- }
- return false;
+ return fitsIntegerType(m_value.numerator(), targetType);
}
case Category::FixedPoint:
{
- if (auto fixed = fixedPointType())
- return fixed->isImplicitlyConvertibleTo(_convertTo);
- return false;
+ FixedPointType const& targetType = dynamic_cast<FixedPointType const&>(_convertTo);
+ // Store a negative number into an unsigned.
+ if (isNegative() && !targetType.isSigned())
+ return false;
+ if (!isFractional())
+ return (targetType.minIntegerValue() <= m_value) && (m_value <= targetType.maxIntegerValue());
+ rational value = m_value * pow(bigint(10), targetType.fractionalDigits());
+ // Need explicit conversion since truncation will occur.
+ if (value.denominator() != 1)
+ return false;
+ return fitsIntoBits(value.numerator(), targetType.numBits(), targetType.isSigned());
}
case Category::FixedBytes:
return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
@@ -995,7 +1002,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
}
}
-bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (isImplicitlyConvertibleTo(_convertTo))
return true;
@@ -1008,7 +1015,7 @@ bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
+TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const
{
rational value;
switch (_operator)
@@ -1029,10 +1036,10 @@ TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
default:
return TypePointer();
}
- return make_shared<RationalNumberType>(value);
+ return TypeResult(make_shared<RationalNumberType>(value));
}
-TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{
@@ -1129,9 +1136,8 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
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();
+ return TypeResult{"Precision of rational constants is limited to 4096 bits."};
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
if (_base == 1)
@@ -1212,9 +1218,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe
// 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 TypeResult{"Precision of rational constants is limited to 4096 bits."};
- return make_shared<RationalNumberType>(value);
+ return TypeResult(make_shared<RationalNumberType>(value));
}
}
@@ -1354,7 +1360,7 @@ StringLiteralType::StringLiteralType(Literal const& _literal):
{
}
-bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto fixedBytes = dynamic_cast<FixedBytesType const*>(&_convertTo))
return size_t(fixedBytes->numBytes()) >= m_value.size();
@@ -1409,7 +1415,7 @@ FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes)
);
}
-bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -1417,7 +1423,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return convertTo.m_bytes >= m_bytes;
}
-bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) ||
(_convertTo.category() == Category::Address && numBytes() == 20) ||
@@ -1425,18 +1431,18 @@ bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
_convertTo.category() == category();
}
-TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const
+TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const
{
// "delete" and "~" is okay for FixedBytesType
if (_operator == Token::Delete)
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
else if (_operator == Token::BitNot)
return shared_from_this();
return TypePointer();
}
-TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (TokenTraits::isShiftOp(_operator))
{
@@ -1452,7 +1458,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer co
// FixedBytes can be compared and have bitwise operators applied to them
if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
- return commonType;
+ return TypeResult(commonType);
return TypePointer();
}
@@ -1486,14 +1492,14 @@ u256 BoolType::literalValue(Literal const* _literal) const
solAssert(false, "Bool type constructed from non-boolean literal.");
}
-TypePointer BoolType::unaryOperatorResult(Token _operator) const
+TypeResult BoolType::unaryOperatorResult(Token _operator) const
{
if (_operator == Token::Delete)
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
}
-TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (category() != _other->category())
return TypePointer();
@@ -1503,7 +1509,7 @@ TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _
return TypePointer();
}
-bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (*this == _convertTo)
return true;
@@ -1520,7 +1526,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
-bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
@@ -1533,14 +1539,14 @@ bool ContractType::isPayable() const
return fallbackFunction && fallbackFunction->isPayable();
}
-TypePointer ContractType::unaryOperatorResult(Token _operator) const
+TypeResult ContractType::unaryOperatorResult(Token _operator) const
{
if (isSuper())
return TypePointer{};
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
+TypeResult ReferenceType::unaryOperatorResult(Token _operator) const
{
if (_operator != Token::Delete)
return TypePointer();
@@ -1551,7 +1557,7 @@ TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
case DataLocation::CallData:
return TypePointer();
case DataLocation::Memory:
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
case DataLocation::Storage:
return m_isPointer ? TypePointer() : make_shared<TupleType>();
}
@@ -1605,7 +1611,7 @@ string ReferenceType::identifierLocationSuffix() const
return id;
}
-bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+BoolResult ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -1645,7 +1651,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
}
}
-bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
+BoolResult ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const
{
if (isImplicitlyConvertibleTo(_convertTo))
return true;
@@ -1815,23 +1821,23 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap members;
if (!isString())
{
- members.push_back({"length", make_shared<IntegerType>(256)});
+ members.emplace_back("length", make_shared<IntegerType>(256));
if (isDynamicallySized() && location() == DataLocation::Storage)
{
- members.push_back({"push", make_shared<FunctionType>(
+ members.emplace_back("push", make_shared<FunctionType>(
TypePointers{baseType()},
TypePointers{make_shared<IntegerType>(256)},
strings{string()},
strings{string()},
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
- )});
- members.push_back({"pop", make_shared<FunctionType>(
+ ));
+ members.emplace_back("pop", make_shared<FunctionType>(
TypePointers{},
TypePointers{},
strings{string()},
strings{string()},
FunctionType::Kind::ArrayPop
- )});
+ ));
}
}
return members;
@@ -1960,21 +1966,17 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
break;
}
if (!functionWithEqualArgumentsFound)
- members.push_back(MemberList::Member(
- function->name(),
- functionType,
- function
- ));
+ members.emplace_back(function->name(), functionType, function);
}
}
else if (!m_contract.isLibrary())
{
for (auto const& it: m_contract.interfaceFunctions())
- members.push_back(MemberList::Member(
+ members.emplace_back(
it.second->declaration().name(),
it.second->asCallableFunction(m_contract.isLibrary()),
&it.second->declaration()
- ));
+ );
}
return members;
}
@@ -2002,11 +2004,11 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
for (size_t index = 0; index < variables.size(); ++index)
if (auto const* offset = offsets.offset(index))
- variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second));
+ variablesAndOffsets.emplace_back(variables[index], offset->first, offset->second);
return variablesAndOffsets;
}
-bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
+BoolResult StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -2092,10 +2094,10 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
// Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
- members.push_back(MemberList::Member(
+ members.emplace_back(
variable->name(),
copyForLocationIfReference(type),
- variable.get())
+ variable.get()
);
}
return members;
@@ -2249,7 +2251,7 @@ bool StructType::recursive() const
return *m_recursive;
}
-TypePointer EnumType::unaryOperatorResult(Token _operator) const
+TypeResult EnumType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
@@ -2291,7 +2293,7 @@ size_t EnumType::numberOfMembers() const
return m_enum.members().size();
};
-bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo == *this || _convertTo.category() == Category::Integer;
}
@@ -2308,7 +2310,7 @@ unsigned EnumType::memberValue(ASTString const& _member) const
solAssert(false, "Requested unknown enum value " + _member);
}
-bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
+BoolResult TupleType::isImplicitlyConvertibleTo(Type const& _other) const
{
if (auto tupleType = dynamic_cast<TupleType const*>(&_other))
{
@@ -2432,7 +2434,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
{
m_parameterTypes.push_back(mappingType->keyType());
- m_parameterNames.push_back("");
+ m_parameterNames.emplace_back("");
returnType = mappingType->valueType();
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
@@ -2441,7 +2443,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
// Return byte arrays as whole.
break;
returnType = arrayType->baseType();
- m_parameterNames.push_back("");
+ m_parameterNames.emplace_back("");
m_parameterTypes.push_back(make_shared<IntegerType>(256));
}
else
@@ -2472,7 +2474,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
DataLocation::Memory,
returnType
));
- m_returnParameterNames.push_back("");
+ m_returnParameterNames.emplace_back("");
}
}
@@ -2648,14 +2650,14 @@ bool FunctionType::operator==(Type const& _other) const
return true;
}
-bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (m_kind == Kind::External && _convertTo == AddressType::address())
return true;
return _convertTo.category() == category();
}
-bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() != category())
return false;
@@ -2680,14 +2682,14 @@ bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true;
}
-TypePointer FunctionType::unaryOperatorResult(Token _operator) const
+TypeResult FunctionType::unaryOperatorResult(Token _operator) const
{
if (_operator == Token::Delete)
- return make_shared<TupleType>();
+ return TypeResult(make_shared<TupleType>());
return TypePointer();
}
-TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+TypeResult FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
return TypePointer();
@@ -2841,14 +2843,11 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
- members.push_back(MemberList::Member(
- "selector",
- make_shared<FixedBytesType>(4)
- ));
+ members.emplace_back("selector", make_shared<FixedBytesType>(4));
if (m_kind != Kind::BareDelegateCall)
{
if (isPayable())
- members.push_back(MemberList::Member(
+ members.emplace_back(
"value",
make_shared<FunctionType>(
parseElementaryTypeVector({"uint"}),
@@ -2862,10 +2861,10 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
m_gasSet,
m_valueSet
)
- ));
+ );
}
if (m_kind != Kind::Creation)
- members.push_back(MemberList::Member(
+ members.emplace_back(
"gas",
make_shared<FunctionType>(
parseElementaryTypeVector({"uint"}),
@@ -2879,7 +2878,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
m_gasSet,
m_valueSet
)
- ));
+ );
return members;
}
default:
@@ -3207,24 +3206,24 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
if (contract.isLibrary())
for (FunctionDefinition const* function: contract.definedFunctions())
if (function->isVisibleAsLibraryMember())
- members.push_back(MemberList::Member(
+ members.emplace_back(
function->name(),
FunctionType(*function).asCallableFunction(true),
function
- ));
+ );
if (isBase)
{
// We are accessing the type of a base contract, so add all public and protected
// members. Note that this does not add inherited functions on purpose.
for (Declaration const* decl: contract.inheritableMembers())
- members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
+ members.emplace_back(decl->name(), decl->type(), decl);
}
else
{
for (auto const& stru: contract.definedStructs())
- members.push_back(MemberList::Member(stru->name(), stru->type(), stru));
+ members.emplace_back(stru->name(), stru->type(), stru);
for (auto const& enu: contract.definedEnums())
- members.push_back(MemberList::Member(enu->name(), enu->type(), enu));
+ members.emplace_back(enu->name(), enu->type(), enu);
}
}
else if (m_actualType->category() == Category::Enum)
@@ -3232,7 +3231,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
auto enumType = make_shared<EnumType>(enumDef);
for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
- members.push_back(MemberList::Member(enumValue->name(), enumType));
+ members.emplace_back(enumValue->name(), enumType);
}
return members;
}
@@ -3297,7 +3296,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap symbols;
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
for (Declaration const* symbol: symbolName.second)
- symbols.push_back(MemberList::Member(symbolName.first, symbol->type(), symbol));
+ symbols.emplace_back(symbolName.first, symbol->type(), symbol);
return symbols;
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 0f0548d3..bee00661 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -22,22 +22,23 @@
#pragma once
-#include <liblangutil/Exceptions.h>
-#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTEnums.h>
+#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/parsing/Token.h>
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
+#include <libdevcore/Result.h>
+#include <boost/optional.hpp>
#include <boost/noncopyable.hpp>
#include <boost/rational.hpp>
-#include <boost/optional.hpp>
-#include <memory>
-#include <string>
#include <map>
+#include <memory>
#include <set>
+#include <string>
namespace dev
{
@@ -50,6 +51,8 @@ using TypePointer = std::shared_ptr<Type const>;
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
using rational = boost::rational<dev::bigint>;
+using TypeResult = Result<TypePointer>;
+using BoolResult = Result<bool>;
inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
{
@@ -63,6 +66,7 @@ inline rational makeRational(bigint const& _numerator, bigint const& _denominato
enum class DataLocation { Storage, CallData, Memory };
+
/**
* Helper class to compute storage offsets of members of structs and contracts.
*/
@@ -189,19 +193,19 @@ public:
/// @returns an escaped identifier (will not contain any parenthesis or commas)
static std::string escapeIdentifier(std::string const& _identifier);
- virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
- virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
+ virtual BoolResult isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
+ virtual BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return isImplicitlyConvertibleTo(_convertTo);
}
/// @returns the resulting type of applying the given unary operator or an empty pointer if
/// this is not possible.
/// The default implementation does not allow any unary operator.
- virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); }
+ virtual TypeResult unaryOperatorResult(Token) const { return TypePointer(); }
/// @returns the resulting type of applying the given binary operator or an empty pointer if
/// this is not possible.
/// The default implementation allows comparison operators if a common type exists
- virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const
+ virtual TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
}
@@ -336,10 +340,10 @@ public:
explicit AddressType(StateMutability _stateMutability);
std::string richIdentifier() const override;
- bool isImplicitlyConvertibleTo(Type const& _other) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
bool operator==(Type const& _other) const override;
@@ -381,10 +385,10 @@ public:
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
std::string richIdentifier() const override;
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
bool operator==(Type const& _other) const override;
@@ -423,10 +427,10 @@ public:
explicit FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, Modifier _modifier = Modifier::Unsigned);
std::string richIdentifier() const override;
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
bool operator==(Type const& _other) const override;
@@ -476,11 +480,10 @@ public:
explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
-
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
@@ -536,8 +539,8 @@ public:
explicit StringLiteralType(Literal const& _literal);
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -570,12 +573,12 @@ public:
explicit FixedBytesType(unsigned _bytes);
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
unsigned storageBytes() const override { return m_bytes; }
@@ -598,11 +601,10 @@ private:
class BoolType: public Type
{
public:
- BoolType() {}
Category category() const override { return Category::Bool; }
std::string richIdentifier() const override { return "t_bool"; }
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
unsigned storageBytes() const override { return 1; }
@@ -624,8 +626,8 @@ public:
explicit ReferenceType(DataLocation _location): m_location(_location) {}
DataLocation location() const { return m_location; }
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -702,8 +704,8 @@ public:
m_length(_length)
{}
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
std::string richIdentifier() const override;
bool operator==(const Type& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override;
@@ -757,10 +759,10 @@ public:
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
m_contract(_contract), m_super(_super) {}
/// Contracts can be implicitly converted only to base contracts.
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
/// Contracts can only be explicitly converted to address types and base contracts.
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded ) const override
@@ -821,7 +823,7 @@ public:
Category category() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
ReferenceType(_location), m_struct(_struct) {}
- bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
+ BoolResult isImplicitlyConvertibleTo(const Type& _convertTo) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override;
@@ -876,7 +878,7 @@ class EnumType: public Type
public:
Category category() const override { return Category::Enum; }
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
- TypePointer unaryOperatorResult(Token _operator) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
unsigned calldataEncodedSize(bool _padded) const override
@@ -889,7 +891,7 @@ public:
std::string canonicalName() const override;
bool isValueType() const override { return true; }
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(8 * int(storageBytes()));
@@ -917,10 +919,10 @@ class TupleType: public Type
public:
Category category() const override { return Category::Tuple; }
explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {}
- bool isImplicitlyConvertibleTo(Type const& _other) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _other) const override;
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
std::string toString(bool) const override;
bool canBeStored() const override { return false; }
u256 storageSize() const override;
@@ -1065,10 +1067,10 @@ public:
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
- bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- TypePointer unaryOperatorResult(Token _operator) const override;
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override;
+ BoolResult isImplicitlyConvertibleTo(Type const& _convertTo) const override;
+ BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
+ TypeResult unaryOperatorResult(Token _operator) const override;
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override;
std::string canonicalName() const override;
std::string toString(bool _short) const override;
unsigned calldataEncodedSize(bool _padded) const override;
@@ -1197,7 +1199,7 @@ public:
std::string toString(bool _short) const override;
std::string canonicalName() const override;
bool canLiveOutsideStorage() const override { return false; }
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(256);
@@ -1230,7 +1232,7 @@ public:
explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
TypePointer const& actualType() const { return m_actualType; }
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; }
@@ -1255,7 +1257,7 @@ public:
Category category() const override { return Category::Modifier; }
explicit ModifierType(ModifierDefinition const& _modifier);
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
@@ -1281,7 +1283,7 @@ public:
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; }
@@ -1308,7 +1310,7 @@ public:
explicit MagicType(Kind _kind): m_kind(_kind) {}
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -1339,9 +1341,9 @@ public:
Category category() const override { return Category::InaccessibleDynamic; }
std::string richIdentifier() const override { return "t_inaccessible"; }
- bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
- bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
- TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
+ BoolResult isImplicitlyConvertibleTo(Type const&) const override { return false; }
+ BoolResult isExplicitlyConvertibleTo(Type const&) const override { return false; }
+ TypeResult binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index b02623de..c1ab03e3 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -24,7 +24,6 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/CompilerUtils.h>
-
#include <libdevcore/Whiskers.h>
#include <boost/algorithm/string/join.hpp>
@@ -141,8 +140,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
vector<string> valueNamesLocal;
for (size_t j = 0; j < sizeOnStack; j++)
{
- valueNamesLocal.push_back("value" + to_string(stackPos));
- valueReturnParams.push_back("value" + to_string(stackPos));
+ valueNamesLocal.emplace_back("value" + to_string(stackPos));
+ valueReturnParams.emplace_back("value" + to_string(stackPos));
stackPos++;
}
bool dynamic = decodingTypes[i]->isDynamicallyEncoded();
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index d2132258..1e0cf7fa 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -22,14 +22,13 @@
#pragma once
-#include <liblangutil/EVMVersion.h>
-
#include <libsolidity/ast/ASTForward.h>
+#include <liblangutil/EVMVersion.h>
-#include <vector>
#include <functional>
-#include <set>
#include <map>
+#include <set>
+#include <vector>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index 4878f9f3..f9678f7c 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -21,13 +21,15 @@
*/
#include <libsolidity/codegen/ArrayUtils.h>
-#include <libevmasm/Instruction.h>
+
+#include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h>
-#include <libsolidity/ast/Types.h>
-#include <liblangutil/Exceptions.h>
#include <libsolidity/codegen/LValue.h>
+#include <libevmasm/Instruction.h>
+#include <liblangutil/Exceptions.h>
+
using namespace std;
using namespace dev;
using namespace langutil;
diff --git a/libsolidity/codegen/AsmCodeGen.cpp b/libsolidity/codegen/AsmCodeGen.cpp
new file mode 100644
index 00000000..c04c1c34
--- /dev/null
+++ b/libsolidity/codegen/AsmCodeGen.cpp
@@ -0,0 +1,197 @@
+/*
+ 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/>.
+*/
+/**
+ * Adaptor between the abstract assembly and eth assembly.
+ */
+
+#include <libsolidity/codegen/AsmCodeGen.h>
+
+#include <libyul/AsmData.h>
+#include <libyul/AsmAnalysisInfo.h>
+
+#include <libyul/backends/evm/AbstractAssembly.h>
+#include <libyul/backends/evm/EVMCodeTransform.h>
+
+#include <libevmasm/Assembly.h>
+#include <libevmasm/AssemblyItem.h>
+#include <libevmasm/Instruction.h>
+
+#include <liblangutil/SourceLocation.h>
+
+#include <libdevcore/FixedHash.h>
+
+#include <memory>
+#include <functional>
+
+using namespace std;
+using namespace dev;
+using namespace langutil;
+using namespace yul;
+using namespace dev::solidity;
+
+EthAssemblyAdapter::EthAssemblyAdapter(eth::Assembly& _assembly):
+ m_assembly(_assembly)
+{
+}
+
+void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
+{
+ m_assembly.setSourceLocation(_location);
+}
+
+int EthAssemblyAdapter::stackHeight() const
+{
+ return m_assembly.deposit();
+}
+
+void EthAssemblyAdapter::appendInstruction(solidity::Instruction _instruction)
+{
+ m_assembly.append(_instruction);
+}
+
+void EthAssemblyAdapter::appendConstant(u256 const& _constant)
+{
+ m_assembly.append(_constant);
+}
+
+void EthAssemblyAdapter::appendLabel(LabelID _labelId)
+{
+ m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
+}
+
+void EthAssemblyAdapter::appendLabelReference(LabelID _labelId)
+{
+ m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
+}
+
+size_t EthAssemblyAdapter::newLabelId()
+{
+ return assemblyTagToIdentifier(m_assembly.newTag());
+}
+
+size_t EthAssemblyAdapter::namedLabel(std::string const& _name)
+{
+ return assemblyTagToIdentifier(m_assembly.namedTag(_name));
+}
+
+void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol)
+{
+ m_assembly.appendLibraryAddress(_linkerSymbol);
+}
+
+void EthAssemblyAdapter::appendJump(int _stackDiffAfter)
+{
+ appendInstruction(solidity::Instruction::JUMP);
+ m_assembly.adjustDeposit(_stackDiffAfter);
+}
+
+void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter)
+{
+ appendLabelReference(_labelId);
+ appendJump(_stackDiffAfter);
+}
+
+void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId)
+{
+ appendLabelReference(_labelId);
+ appendInstruction(solidity::Instruction::JUMPI);
+}
+
+void EthAssemblyAdapter::appendBeginsub(LabelID, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "BEGINSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendJumpsub(LabelID, int, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "JUMPSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendReturnsub(int, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "RETURNSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendAssemblySize()
+{
+ m_assembly.appendProgramSize();
+}
+
+pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly()
+{
+ shared_ptr<eth::Assembly> assembly{make_shared<eth::Assembly>()};
+ auto sub = m_assembly.newSub(assembly);
+ return {make_shared<EthAssemblyAdapter>(*assembly), size_t(sub.data())};
+}
+
+void EthAssemblyAdapter::appendDataOffset(AbstractAssembly::SubID _sub)
+{
+ auto it = m_dataHashBySubId.find(_sub);
+ if (it == m_dataHashBySubId.end())
+ m_assembly.pushSubroutineOffset(size_t(_sub));
+ else
+ m_assembly << eth::AssemblyItem(eth::PushData, it->second);
+}
+
+void EthAssemblyAdapter::appendDataSize(AbstractAssembly::SubID _sub)
+{
+ auto it = m_dataHashBySubId.find(_sub);
+ if (it == m_dataHashBySubId.end())
+ m_assembly.pushSubroutineSize(size_t(_sub));
+ else
+ m_assembly << u256(m_assembly.data(h256(it->second)).size());
+}
+
+AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data)
+{
+ eth::AssemblyItem pushData = m_assembly.newData(_data);
+ SubID subID = m_nextDataCounter++;
+ m_dataHashBySubId[subID] = pushData.data();
+ return subID;
+}
+
+EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
+{
+ u256 id = _tag.data();
+ solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
+ return LabelID(id);
+}
+
+void CodeGenerator::assemble(
+ Block const& _parsedData,
+ AsmAnalysisInfo& _analysisInfo,
+ eth::Assembly& _assembly,
+ ExternalIdentifierAccess const& _identifierAccess,
+ bool _useNamedLabelsForFunctions,
+ bool _optimize
+)
+{
+ EthAssemblyAdapter assemblyAdapter(_assembly);
+ CodeTransform(
+ assemblyAdapter,
+ _analysisInfo,
+ _parsedData,
+ *EVMDialect::strictAssemblyForEVM(),
+ _optimize,
+ false,
+ _identifierAccess,
+ _useNamedLabelsForFunctions
+ )(_parsedData);
+}
diff --git a/libsolidity/codegen/AsmCodeGen.h b/libsolidity/codegen/AsmCodeGen.h
new file mode 100644
index 00000000..516b0a36
--- /dev/null
+++ b/libsolidity/codegen/AsmCodeGen.h
@@ -0,0 +1,92 @@
+/*
+ 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/>.
+*/
+/**
+ * Adaptor between the abstract assembly and eth assembly.
+ */
+
+#pragma once
+
+#include <libyul/AsmAnalysis.h>
+#include <libyul/backends/evm/AbstractAssembly.h>
+#include <liblangutil/SourceLocation.h>
+#include <functional>
+
+namespace yul
+{
+struct Block;
+}
+
+namespace dev
+{
+namespace eth
+{
+class Assembly;
+class AssemblyItem;
+}
+
+namespace solidity
+{
+
+class EthAssemblyAdapter: public yul::AbstractAssembly
+{
+public:
+ explicit EthAssemblyAdapter(eth::Assembly& _assembly);
+ void setSourceLocation(langutil::SourceLocation const& _location) override;
+ int stackHeight() const override;
+ void appendInstruction(solidity::Instruction _instruction) override;
+ void appendConstant(u256 const& _constant) override;
+ void appendLabel(LabelID _labelId) override;
+ void appendLabelReference(LabelID _labelId) override;
+ size_t newLabelId() override;
+ size_t namedLabel(std::string const& _name) override;
+ void appendLinkerSymbol(std::string const& _linkerSymbol) override;
+ void appendJump(int _stackDiffAfter) override;
+ void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override;
+ void appendJumpToIf(LabelID _labelId) override;
+ void appendBeginsub(LabelID, int) override;
+ void appendJumpsub(LabelID, int, int) override;
+ void appendReturnsub(int, int) override;
+ void appendAssemblySize() override;
+ std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
+ void appendDataOffset(SubID _sub) override;
+ void appendDataSize(SubID _sub) override;
+ SubID appendData(dev::bytes const& _data) override;
+
+private:
+ static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag);
+
+ eth::Assembly& m_assembly;
+ std::map<SubID, dev::u256> m_dataHashBySubId;
+ size_t m_nextDataCounter = std::numeric_limits<size_t>::max() / 2;
+};
+
+class CodeGenerator
+{
+public:
+ /// Performs code generation and appends generated to _assembly.
+ static void assemble(
+ yul::Block const& _parsedData,
+ yul::AsmAnalysisInfo& _analysisInfo,
+ dev::eth::Assembly& _assembly,
+ yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(),
+ bool _useNamedLabelsForFunctions = false,
+ bool _optimize = false
+ );
+};
+
+}
+}
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index 55f1d252..a22e6e9d 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -21,8 +21,9 @@
*/
#include <libsolidity/codegen/Compiler.h>
-#include <libevmasm/Assembly.h>
+
#include <libsolidity/codegen/ContractCompiler.h>
+#include <libevmasm/Assembly.h>
using namespace std;
using namespace dev;
@@ -34,13 +35,13 @@ void Compiler::compileContract(
bytes const& _metadata
)
{
- ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
+ ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
runtimeCompiler.compileContract(_contract, _contracts);
m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at
// creation time.
- ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
+ ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1);
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
m_context.optimise(m_optimize, m_optimizeRuns);
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index 48d9e9d6..784d7f8c 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -24,11 +24,9 @@
#include <libsolidity/codegen/CompilerContext.h>
#include <liblangutil/EVMVersion.h>
-
#include <libevmasm/Assembly.h>
-
-#include <ostream>
#include <functional>
+#include <ostream>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index 5a3a233c..be681b2e 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -21,18 +21,22 @@
*/
#include <libsolidity/codegen/CompilerContext.h>
-#include <libsolidity/codegen/CompilerUtils.h>
+
#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/AsmCodeGen.h>
#include <libsolidity/codegen/Compiler.h>
+#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/interface/Version.h>
-#include <liblangutil/SourceReferenceFormatter.h>
+
#include <libyul/AsmParser.h>
-#include <libyul/AsmCodeGen.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/YulString.h>
+
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h>
+#include <liblangutil/SourceReferenceFormatter.h>
#include <boost/algorithm/string/replace.hpp>
@@ -361,7 +365,7 @@ void CompilerContext::appendInlineAssembly(
ErrorList errors;
ErrorReporter errorReporter(errors);
auto scanner = make_shared<langutil::Scanner>(langutil::CharStream(_assembly, "--CODEGEN--"));
- auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false);
+ auto parserResult = yul::Parser(errorReporter, yul::EVMDialect::strictAssemblyForEVM()).parse(scanner, false);
#ifdef SOL_OUTPUT_ASM
cout << yul::AsmPrinter()(*parserResult) << endl;
#endif
@@ -373,7 +377,7 @@ void CompilerContext::appendInlineAssembly(
errorReporter,
m_evmVersion,
boost::none,
- yul::AsmFlavour::Strict,
+ yul::EVMDialect::strictAssemblyForEVM(),
identifierAccess.resolve
).analyze(*parserResult);
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
@@ -386,8 +390,7 @@ void CompilerContext::appendInlineAssembly(
for (auto const& error: errorReporter.errors())
message += SourceReferenceFormatter::formatExceptionInformation(
*error,
- (error->type() == Error::Type::Warning) ? "Warning" : "Error",
- [&](string const&) -> Scanner const& { return *scanner; }
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error"
);
message += "-------------------------------------------\n";
@@ -395,7 +398,7 @@ void CompilerContext::appendInlineAssembly(
}
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
- yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
+ CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
// Reset the source location to the one of the node (instead of the CODEGEN source location)
updateSourceLocation();
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 02369813..dedcd95f 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -22,24 +22,21 @@
#pragma once
-#include <libsolidity/codegen/ABIFunctions.h>
-
-#include <liblangutil/EVMVersion.h>
-
+#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/Types.h>
-#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/codegen/ABIFunctions.h>
-#include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h>
-
+#include <libevmasm/Instruction.h>
+#include <liblangutil/EVMVersion.h>
#include <libdevcore/Common.h>
+#include <functional>
#include <ostream>
#include <stack>
#include <queue>
#include <utility>
-#include <functional>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 7d2ad9d2..bbc703c7 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -23,12 +23,10 @@
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/ABIFunctions.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;
@@ -1205,7 +1203,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
{
//@todo provide both alternatives to the optimiser
// stack: mempos
- if (_data.size() <= 128)
+ if (_data.size() <= 32)
{
for (unsigned i = 0; i < _data.size(); i += 32)
{
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 5f7dce22..7e4f47ba 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -22,8 +22,8 @@
#pragma once
-#include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/codegen/CompilerContext.h>
namespace dev {
namespace solidity {
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index aabdbb79..b051d260 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -20,19 +20,18 @@
* Solidity compiler.
*/
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/AsmCodeGen.h>
+#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ContractCompiler.h>
#include <libsolidity/codegen/ExpressionCompiler.h>
-#include <libsolidity/codegen/CompilerUtils.h>
-#include <libsolidity/ast/AST.h>
-#include <libyul/AsmCodeGen.h>
-#include <liblangutil/ErrorReporter.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/GasMeter.h>
+#include <liblangutil/ErrorReporter.h>
#include <boost/range/adaptor/reversed.hpp>
-
#include <algorithm>
using namespace std;
@@ -268,6 +267,89 @@ void ContractCompiler::appendDelegatecallCheck()
// "We have not been called via DELEGATECALL".
}
+void ContractCompiler::appendInternalSelector(
+ map<FixedHash<4>, eth::AssemblyItem const> const& _entryPoints,
+ vector<FixedHash<4>> const& _ids,
+ eth::AssemblyItem const& _notFoundTag,
+ size_t _runs
+)
+{
+ // Code for selecting from n functions without split:
+ // n times: dup1, push4 <id_i>, eq, push2/3 <tag_i>, jumpi
+ // push2/3 <notfound> jump
+ // (called SELECT[n])
+ // Code for selecting from n functions with split:
+ // dup1, push4 <pivot>, gt, push2/3<tag_less>, jumpi
+ // SELECT[n/2]
+ // tag_less:
+ // SELECT[n/2]
+ //
+ // This means each split adds 16-18 bytes of additional code (note the additional jump out!)
+ // The average execution cost if we do not split at all are:
+ // (3 + 3 + 3 + 3 + 10) * n/2 = 24 * n/2 = 12 * n
+ // If we split once:
+ // (3 + 3 + 3 + 3 + 10) + 24 * n/4 = 24 * (n/4 + 1) = 6 * n + 24;
+ //
+ // We should split if
+ // _runs * 12 * n > _runs * (6 * n + 24) + 17 * createDataGas
+ // <=> _runs * 6 * (n - 4) > 17 * createDataGas
+ //
+ // Which also means that the execution itself is not profitable
+ // unless we have at least 5 functions.
+
+ // Start with some comparisons to avoid overflow, then do the actual comparison.
+ bool split = false;
+ if (_ids.size() <= 4)
+ split = false;
+ else if (_runs > (17 * eth::GasCosts::createDataGas) / 6)
+ split = true;
+ else
+ split = (_runs * 6 * (_ids.size() - 4) > 17 * eth::GasCosts::createDataGas);
+
+ if (split)
+ {
+ size_t pivotIndex = _ids.size() / 2;
+ FixedHash<4> pivot{_ids.at(pivotIndex)};
+ m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(pivot)) << Instruction::GT;
+ eth::AssemblyItem lessTag{m_context.appendConditionalJump()};
+ // Here, we have funid >= pivot
+ vector<FixedHash<4>> larger{_ids.begin() + pivotIndex, _ids.end()};
+ appendInternalSelector(_entryPoints, larger, _notFoundTag, _runs);
+ m_context << lessTag;
+ // Here, we have funid < pivot
+ vector<FixedHash<4>> smaller{_ids.begin(), _ids.begin() + pivotIndex};
+ appendInternalSelector(_entryPoints, smaller, _notFoundTag, _runs);
+ }
+ else
+ {
+ for (auto const& id: _ids)
+ {
+ m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(id)) << Instruction::EQ;
+ m_context.appendConditionalJumpTo(_entryPoints.at(id));
+ }
+ m_context.appendJumpTo(_notFoundTag);
+ }
+}
+
+namespace
+{
+
+// Helper function to check if any function is payable
+bool hasPayableFunctions(ContractDefinition const& _contract)
+{
+ FunctionDefinition const* fallback = _contract.fallbackFunction();
+ if (fallback && fallback->isPayable())
+ return true;
+
+ for (auto const& it: _contract.interfaceFunctions())
+ if (it.second->isPayable())
+ return true;
+
+ return false;
+}
+
+}
+
void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions();
@@ -279,6 +361,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
}
FunctionDefinition const* fallback = _contract.fallbackFunction();
+ solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have fallback functions");
+
+ bool needToAddCallvalueCheck = true;
+ if (!hasPayableFunctions(_contract) && !interfaceFunctions.empty() && !_contract.isLibrary())
+ {
+ appendCallValueCheck();
+ needToAddCallvalueCheck = false;
+ }
+
eth::AssemblyItem notFound = m_context.newTag();
// directly jump to fallback if the data is too short to contain a function selector
// also guards against short data
@@ -287,22 +378,26 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
// retrieve the function signature hash from the calldata
if (!interfaceFunctions.empty())
+ {
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
- // stack now is: <can-call-non-view-functions>? <funhash>
- for (auto const& it: interfaceFunctions)
- {
- callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
- m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(it.first)) << Instruction::EQ;
- m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.at(it.first));
+ // stack now is: <can-call-non-view-functions>? <funhash>
+ vector<FixedHash<4>> sortedIDs;
+ for (auto const& it: interfaceFunctions)
+ {
+ callDataUnpackerEntryPoints.emplace(it.first, m_context.newTag());
+ sortedIDs.emplace_back(it.first);
+ }
+ std::sort(sortedIDs.begin(), sortedIDs.end());
+ appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs);
}
- m_context.appendJumpTo(notFound);
m_context << notFound;
+
if (fallback)
{
solAssert(!_contract.isLibrary(), "");
- if (!fallback->isPayable())
+ if (!fallback->isPayable() && needToAddCallvalueCheck)
appendCallValueCheck();
solAssert(fallback->isFallback(), "");
@@ -332,7 +427,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context.setStackOffset(0);
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
- if (!functionType->isPayable() && !_contract.isLibrary())
+ if (!functionType->isPayable() && !_contract.isLibrary() && needToAddCallvalueCheck)
appendCallValueCheck();
// Return tag is used to jump out of the function.
@@ -618,7 +713,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
}
};
solAssert(_inlineAssembly.annotation().analysisInfo, "");
- yul::CodeGenerator::assemble(
+ CodeGenerator::assemble(
_inlineAssembly.operations(),
*_inlineAssembly.annotation().analysisInfo,
m_context.nonConstAssembly(),
@@ -656,14 +751,14 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
- m_breakTags.push_back({loopEnd, m_context.stackHeight()});
+ m_breakTags.emplace_back(loopEnd, m_context.stackHeight());
m_context << loopStart;
if (_whileStatement.isDoWhile())
{
eth::AssemblyItem condition = m_context.newTag();
- m_continueTags.push_back({condition, m_context.stackHeight()});
+ m_continueTags.emplace_back(condition, m_context.stackHeight());
_whileStatement.body().accept(*this);
@@ -674,7 +769,7 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
}
else
{
- m_continueTags.push_back({loopStart, m_context.stackHeight()});
+ m_continueTags.emplace_back(loopStart, m_context.stackHeight());
compileExpression(_whileStatement.condition());
m_context << Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
@@ -705,8 +800,8 @@ bool ContractCompiler::visit(ForStatement const& _forStatement)
if (_forStatement.initializationExpression())
_forStatement.initializationExpression()->accept(*this);
- m_breakTags.push_back({loopEnd, m_context.stackHeight()});
- m_continueTags.push_back({loopNext, m_context.stackHeight()});
+ m_breakTags.emplace_back(loopEnd, m_context.stackHeight());
+ m_continueTags.emplace_back(loopNext, m_context.stackHeight());
m_context << loopStart;
// if there is no terminating condition in for, default is to always be true
@@ -932,7 +1027,7 @@ void ContractCompiler::appendModifierOrFunctionCode()
if (codeBlock)
{
- m_returnTags.push_back({m_context.newTag(), m_context.stackHeight()});
+ m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
codeBlock->accept(*this);
solAssert(!m_returnTags.empty(), "");
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 001aec7c..40871f0d 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -22,11 +22,11 @@
#pragma once
-#include <ostream>
-#include <functional>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/CompilerContext.h>
#include <libevmasm/Assembly.h>
+#include <functional>
+#include <ostream>
namespace dev {
namespace solidity {
@@ -38,8 +38,9 @@ namespace solidity {
class ContractCompiler: private ASTConstVisitor
{
public:
- explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise):
+ explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200):
m_optimise(_optimise),
+ m_optimise_runs(_optimise_runs),
m_runtimeCompiler(_runtimeCompiler),
m_context(_context)
{
@@ -81,6 +82,14 @@ private:
/// This is done by inserting a specific push constant as the first instruction
/// whose data will be modified in memory at deploy time.
void appendDelegatecallCheck();
+ /// Appends the function selector. Is called recursively to create a binary search tree.
+ /// @a _runs the number of intended executions of the contract to tune the split point.
+ void appendInternalSelector(
+ std::map<FixedHash<4>, eth::AssemblyItem const> const& _entryPoints,
+ std::vector<FixedHash<4>> const& _ids,
+ eth::AssemblyItem const& _notFoundTag,
+ size_t _runs
+ );
void appendFunctionSelector(ContractDefinition const& _contract);
void appendCallValueCheck();
void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary);
@@ -122,6 +131,7 @@ private:
void storeStackHeight(ASTNode const* _node);
bool const m_optimise;
+ size_t const m_optimise_runs = 200;
/// Pointer to the runtime compiler in case this is a creation compiler.
ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context;
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 121585d9..be2709ae 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -20,21 +20,23 @@
* Solidity AST to EVM bytecode compiler for expressions.
*/
-#include <utility>
-#include <numeric>
-#include <boost/range/adaptor/reversed.hpp>
-#include <boost/algorithm/string/replace.hpp>
-#include <libdevcore/Common.h>
-#include <libdevcore/Keccak256.h>
-#include <libsolidity/ast/AST.h>
#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 <libevmasm/GasMeter.h>
+#include <libdevcore/Common.h>
+#include <libdevcore/Keccak256.h>
#include <libdevcore/Whiskers.h>
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/range/adaptor/reversed.hpp>
+#include <numeric>
+#include <utility>
+
using namespace std;
using namespace langutil;
@@ -833,10 +835,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::RIPEMD160:
{
_functionCall.expression().accept(*this);
- static const map<FunctionType::Kind, u256> contractAddresses{{FunctionType::Kind::ECRecover, 1},
- {FunctionType::Kind::SHA256, 2},
- {FunctionType::Kind::RIPEMD160, 3}};
- m_context << contractAddresses.find(function.kind())->second;
+ static map<FunctionType::Kind, u256> const contractAddresses{
+ {FunctionType::Kind::ECRecover, 1},
+ {FunctionType::Kind::SHA256, 2},
+ {FunctionType::Kind::RIPEMD160, 3}
+ };
+ m_context << contractAddresses.at(function.kind());
for (unsigned i = function.sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i);
appendExternalFunctionCall(function, arguments);
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index 2bfaab43..4a6e43ff 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -21,14 +21,17 @@
* Solidity AST to EVM bytecode compiler for expressions.
*/
-#include <functional>
-#include <memory>
-#include <boost/noncopyable.hpp>
-#include <libdevcore/Common.h>
-#include <liblangutil/SourceLocation.h>
+#pragma once
+
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/LValue.h>
#include <liblangutil/Exceptions.h>
+#include <liblangutil/SourceLocation.h>
+#include <libdevcore/Common.h>
+
+#include <boost/noncopyable.hpp>
+#include <functional>
+#include <memory>
namespace dev {
namespace eth
diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp
index 6d71d36f..70dbee81 100644
--- a/libsolidity/codegen/LValue.cpp
+++ b/libsolidity/codegen/LValue.cpp
@@ -21,10 +21,11 @@
*/
#include <libsolidity/codegen/LValue.h>
-#include <libevmasm/Instruction.h>
-#include <libsolidity/ast/Types.h>
+
#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/CompilerUtils.h>
+#include <libevmasm/Instruction.h>
using namespace std;
using namespace dev;
diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h
index d854857b..3072ff11 100644
--- a/libsolidity/codegen/LValue.h
+++ b/libsolidity/codegen/LValue.h
@@ -22,10 +22,10 @@
#pragma once
+#include <libsolidity/codegen/ArrayUtils.h>
+#include <liblangutil/SourceLocation.h>
#include <memory>
#include <vector>
-#include <liblangutil/SourceLocation.h>
-#include <libsolidity/codegen/ArrayUtils.h>
namespace dev
{
@@ -49,7 +49,7 @@ protected:
m_context(_compilerContext), m_dataType(_dataType) {}
public:
- virtual ~LValue() {}
+ virtual ~LValue() = default;
/// @returns the number of stack slots occupied by the lvalue reference
virtual unsigned sizeOnStack() const { return 1; }
/// Copies the value of the current lvalue to the top of the stack and, if @a _remove is true,
diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp
index de5e4430..e7c8f015 100644
--- a/libsolidity/formal/CVC4Interface.cpp
+++ b/libsolidity/formal/CVC4Interface.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/CVC4Interface.h>
#include <liblangutil/Exceptions.h>
-
#include <libdevcore/CommonIO.h>
using namespace std;
diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h
index bbe23855..89792364 100644
--- a/libsolidity/formal/CVC4Interface.h
+++ b/libsolidity/formal/CVC4Interface.h
@@ -18,7 +18,6 @@
#pragma once
#include <libsolidity/formal/SolverInterface.h>
-
#include <boost/noncopyable.hpp>
#if defined(__GLIBC__)
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index ebb09f0a..35c1e2f1 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -18,11 +18,11 @@
#include <libsolidity/formal/SMTChecker.h>
#include <libsolidity/formal/SMTPortfolio.h>
-
#include <libsolidity/formal/VariableUsage.h>
#include <libsolidity/formal/SymbolicTypes.h>
#include <liblangutil/ErrorReporter.h>
+#include <libdevcore/StringUtils.h>
#include <boost/range/adaptor/map.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -58,8 +58,7 @@ void SMTChecker::analyze(SourceUnit const& _source, shared_ptr<Scanner> const& _
bool SMTChecker::visit(ContractDefinition const& _contract)
{
for (auto _var : _contract.stateVariables())
- if (_var->type()->isValueType())
- createVariable(*_var);
+ createVariable(*_var);
return true;
}
@@ -88,14 +87,14 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
m_interface->reset();
m_pathConditions.clear();
m_expressions.clear();
- m_specialVariables.clear();
- m_uninterpretedFunctions.clear();
+ m_globalContext.clear();
m_uninterpretedTerms.clear();
resetStateVariables();
initializeLocalVariables(_function);
+ m_loopExecutionHappened = false;
+ m_arrayAssignmentHappened = false;
}
- m_loopExecutionHappened = false;
return true;
}
@@ -136,13 +135,24 @@ bool SMTChecker::visit(IfStatement const& _node)
return false;
}
+// Here we consider the execution of two branches:
+// Branch 1 assumes the loop condition to be true and executes the loop once,
+// after resetting touched variables.
+// Branch 2 assumes the loop condition to be false and skips the loop after
+// visiting the condition (it might contain side-effects, they need to be considered)
+// and does not erase knowledge.
+// If the loop is a do-while, condition side-effects are lost since the body,
+// executed once before the condition, might reassign variables.
+// Variables touched by the loop are merged with Branch 2.
bool SMTChecker::visit(WhileStatement const& _node)
{
+ auto indicesBeforeLoop = copyVariableIndices();
auto touchedVariables = m_variableUsage->touchedVariables(_node);
resetVariables(touchedVariables);
+ decltype(indicesBeforeLoop) indicesAfterLoop;
if (_node.isDoWhile())
{
- visitBranch(_node.body());
+ indicesAfterLoop = visitBranch(_node.body());
// TODO the assertions generated in the body should still be active in the condition
_node.condition().accept(*this);
if (isRootFunction())
@@ -154,19 +164,31 @@ bool SMTChecker::visit(WhileStatement const& _node)
if (isRootFunction())
checkBooleanNotConstant(_node.condition(), "While loop condition is always $VALUE.");
- visitBranch(_node.body(), expr(_node.condition()));
+ indicesAfterLoop = visitBranch(_node.body(), expr(_node.condition()));
}
- m_loopExecutionHappened = true;
- resetVariables(touchedVariables);
+ // We reset the execution to before the loop
+ // and visit the condition in case it's not a do-while.
+ // A do-while's body might have non-precise information
+ // in its first run about variables that are touched.
+ resetVariableIndices(indicesBeforeLoop);
+ if (!_node.isDoWhile())
+ _node.condition().accept(*this);
+
+ mergeVariables(touchedVariables, expr(_node.condition()), indicesAfterLoop, copyVariableIndices());
+
+ m_loopExecutionHappened = true;
return false;
}
+// Here we consider the execution of two branches similar to WhileStatement.
bool SMTChecker::visit(ForStatement const& _node)
{
if (_node.initializationExpression())
_node.initializationExpression()->accept(*this);
+ auto indicesBeforeLoop = copyVariableIndices();
+
// Do not reset the init expression part.
auto touchedVariables =
m_variableUsage->touchedVariables(_node.body());
@@ -193,13 +215,19 @@ bool SMTChecker::visit(ForStatement const& _node)
_node.body().accept(*this);
if (_node.loopExpression())
_node.loopExpression()->accept(*this);
-
m_interface->pop();
- m_loopExecutionHappened = true;
+ auto indicesAfterLoop = copyVariableIndices();
+ // We reset the execution to before the loop
+ // and visit the condition.
+ resetVariableIndices(indicesBeforeLoop);
+ if (_node.condition())
+ _node.condition()->accept(*this);
- resetVariables(touchedVariables);
+ auto forCondition = _node.condition() ? expr(*_node.condition()) : smt::Expression(true);
+ mergeVariables(touchedVariables, forCondition, indicesAfterLoop, copyVariableIndices());
+ m_loopExecutionHappened = true;
return false;
}
@@ -237,16 +265,14 @@ void SMTChecker::endVisit(Assignment const& _assignment)
else if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_assignment.leftHandSide()))
{
VariableDeclaration const& decl = dynamic_cast<VariableDeclaration const&>(*identifier->annotation().referencedDeclaration);
- if (knownVariable(decl))
- {
- assignment(decl, _assignment.rightHandSide(), _assignment.location());
- defineExpr(_assignment, expr(_assignment.rightHandSide()));
- }
- else
- m_errorReporter.warning(
- _assignment.location(),
- "Assertion checker does not yet implement such assignments."
- );
+ solAssert(knownVariable(decl), "");
+ assignment(decl, _assignment.rightHandSide(), _assignment.location());
+ defineExpr(_assignment, expr(_assignment.rightHandSide()));
+ }
+ else if (dynamic_cast<IndexAccess const*>(&_assignment.leftHandSide()))
+ {
+ arrayIndexAssignment(_assignment);
+ defineExpr(_assignment, expr(_assignment.rightHandSide()));
}
else
m_errorReporter.warning(
@@ -257,7 +283,11 @@ void SMTChecker::endVisit(Assignment const& _assignment)
void SMTChecker::endVisit(TupleExpression const& _tuple)
{
- if (_tuple.isInlineArray() || _tuple.components().size() != 1)
+ if (
+ _tuple.isInlineArray() ||
+ _tuple.components().size() != 1 ||
+ !isSupportedType(_tuple.components()[0]->annotation().type->category())
+ )
m_errorReporter.warning(
_tuple.location(),
"Assertion checker does not yet implement tuples and inline arrays."
@@ -271,14 +301,14 @@ void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _
checkCondition(
_value < minValue(_type),
_location,
- "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")",
+ "Underflow (resulting value less than " + formatNumberReadable(_type.minValue()) + ")",
"<result>",
&_value
);
checkCondition(
_value > maxValue(_type),
_location,
- "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")",
+ "Overflow (resulting value larger than " + formatNumberReadable(_type.maxValue()) + ")",
"<result>",
&_value
);
@@ -368,18 +398,30 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
std::vector<ASTPointer<Expression const>> const args = _funCall.arguments();
- if (funType.kind() == FunctionType::Kind::Assert)
+ switch (funType.kind())
+ {
+ case FunctionType::Kind::Assert:
visitAssert(_funCall);
- else if (funType.kind() == FunctionType::Kind::Require)
+ break;
+ case FunctionType::Kind::Require:
visitRequire(_funCall);
- else if (funType.kind() == FunctionType::Kind::GasLeft)
+ break;
+ case FunctionType::Kind::GasLeft:
visitGasLeft(_funCall);
- else if (funType.kind() == FunctionType::Kind::BlockHash)
- visitBlockHash(_funCall);
- else if (funType.kind() == FunctionType::Kind::Internal)
+ break;
+ case FunctionType::Kind::Internal:
inlineFunctionCall(_funCall);
- else
- {
+ break;
+ case FunctionType::Kind::KECCAK256:
+ case FunctionType::Kind::ECRecover:
+ case FunctionType::Kind::SHA256:
+ case FunctionType::Kind::RIPEMD160:
+ case FunctionType::Kind::BlockHash:
+ case FunctionType::Kind::AddMod:
+ case FunctionType::Kind::MulMod:
+ abstractFunctionCall(_funCall);
+ break;
+ default:
m_errorReporter.warning(
_funCall.location(),
"Assertion checker does not yet implement this type of function call."
@@ -411,8 +453,8 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall)
string gasLeft = "gasleft()";
// We increase the variable index since gasleft changes
// inside a tx.
- defineSpecialVariable(gasLeft, _funCall, true);
- auto const& symbolicVar = m_specialVariables.at(gasLeft);
+ defineGlobalVariable(gasLeft, _funCall, true);
+ auto const& symbolicVar = m_globalContext.at(gasLeft);
unsigned index = symbolicVar->index();
// We set the current value to unknown anyway to add type constraints.
setUnknownValue(*symbolicVar);
@@ -420,19 +462,11 @@ void SMTChecker::visitGasLeft(FunctionCall const& _funCall)
m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1));
}
-void SMTChecker::visitBlockHash(FunctionCall const& _funCall)
+void SMTChecker::eraseArrayKnowledge()
{
- string blockHash = "blockhash";
- auto const& arguments = _funCall.arguments();
- solAssert(arguments.size() == 1, "");
- smt::SortPointer paramSort = smtSort(*arguments.at(0)->annotation().type);
- smt::SortPointer returnSort = smtSort(*_funCall.annotation().type);
- defineUninterpretedFunction(
- blockHash,
- make_shared<smt::FunctionSort>(vector<smt::SortPointer>{paramSort}, returnSort)
- );
- defineExpr(_funCall, m_uninterpretedFunctions.at(blockHash)({expr(*arguments.at(0))}));
- m_uninterpretedTerms.push_back(&_funCall);
+ for (auto const& var: m_variables)
+ if (var.first->annotation().type->category() == Type::Category::Mapping)
+ newValue(*var.first);
}
void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
@@ -502,29 +536,32 @@ void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall)
}
}
+void SMTChecker::abstractFunctionCall(FunctionCall const& _funCall)
+{
+ vector<smt::Expression> smtArguments;
+ for (auto const& arg: _funCall.arguments())
+ smtArguments.push_back(expr(*arg));
+ defineExpr(_funCall, (*m_expressions.at(&_funCall.expression()))(smtArguments));
+ m_uninterpretedTerms.insert(&_funCall);
+ setSymbolicUnknownValue(expr(_funCall), _funCall.annotation().type, *m_interface);
+}
+
void SMTChecker::endVisit(Identifier const& _identifier)
{
if (_identifier.annotation().lValueRequested)
{
// Will be translated as part of the node that requested the lvalue.
}
- else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
+ else if (dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
{
- if (
- fun->kind() == FunctionType::Kind::Assert ||
- fun->kind() == FunctionType::Kind::Require ||
- fun->kind() == FunctionType::Kind::GasLeft ||
- fun->kind() == FunctionType::Kind::BlockHash
- )
- return;
- createExpr(_identifier);
+ visitFunctionIdentifier(_identifier);
}
else if (isSupportedType(_identifier.annotation().type->category()))
{
if (VariableDeclaration const* decl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
defineExpr(_identifier, currentValue(*decl));
else if (_identifier.name() == "now")
- defineSpecialVariable(_identifier.name(), _identifier);
+ defineGlobalVariable(_identifier.name(), _identifier);
else
// TODO: handle MagicVariableDeclaration here
m_errorReporter.warning(
@@ -534,6 +571,20 @@ void SMTChecker::endVisit(Identifier const& _identifier)
}
}
+void SMTChecker::visitFunctionIdentifier(Identifier const& _identifier)
+{
+ auto const& fType = dynamic_cast<FunctionType const&>(*_identifier.annotation().type);
+ if (fType.returnParameterTypes().size() > 1)
+ {
+ m_errorReporter.warning(
+ _identifier.location(),
+ "Assertion checker does not yet support functions with more than one return parameter."
+ );
+ }
+ defineGlobalFunction(fType.richIdentifier(), _identifier);
+ m_expressions.emplace(&_identifier, m_globalContext.at(fType.richIdentifier()));
+}
+
void SMTChecker::endVisit(Literal const& _literal)
{
Type const& type = *_literal.annotation().type;
@@ -585,7 +636,7 @@ bool SMTChecker::visit(MemberAccess const& _memberAccess)
_memberAccess.location(),
"Assertion checker does not yet support this expression."
);
- defineSpecialVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess);
+ defineGlobalVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess);
return false;
}
else
@@ -597,30 +648,107 @@ bool SMTChecker::visit(MemberAccess const& _memberAccess)
return true;
}
-void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
+void SMTChecker::endVisit(IndexAccess const& _indexAccess)
{
- if (!knownSpecialVariable(_name))
+ shared_ptr<SymbolicVariable> array;
+ if (auto const& id = dynamic_cast<Identifier const*>(&_indexAccess.baseExpression()))
+ {
+ auto const& varDecl = dynamic_cast<VariableDeclaration const&>(*id->annotation().referencedDeclaration);
+ solAssert(knownVariable(varDecl), "");
+ array = m_variables[&varDecl];
+ }
+ else if (auto const& innerAccess = dynamic_cast<IndexAccess const*>(&_indexAccess.baseExpression()))
+ {
+ solAssert(knownExpr(*innerAccess), "");
+ array = m_expressions[innerAccess];
+ }
+ else
+ {
+ m_errorReporter.warning(
+ _indexAccess.location(),
+ "Assertion checker does not yet implement this expression."
+ );
+ return;
+ }
+
+ solAssert(array, "");
+ defineExpr(_indexAccess, smt::Expression::select(
+ array->currentValue(),
+ expr(*_indexAccess.indexExpression())
+ ));
+ setSymbolicUnknownValue(
+ expr(_indexAccess),
+ _indexAccess.annotation().type,
+ *m_interface
+ );
+ m_uninterpretedTerms.insert(&_indexAccess);
+}
+
+void SMTChecker::arrayAssignment()
+{
+ m_arrayAssignmentHappened = true;
+ eraseArrayKnowledge();
+}
+
+void SMTChecker::arrayIndexAssignment(Assignment const& _assignment)
+{
+ auto const& indexAccess = dynamic_cast<IndexAccess const&>(_assignment.leftHandSide());
+ if (auto const& id = dynamic_cast<Identifier const*>(&indexAccess.baseExpression()))
+ {
+ auto const& varDecl = dynamic_cast<VariableDeclaration const&>(*id->annotation().referencedDeclaration);
+ solAssert(knownVariable(varDecl), "");
+ smt::Expression store = smt::Expression::store(
+ m_variables[&varDecl]->currentValue(),
+ expr(*indexAccess.indexExpression()),
+ expr(_assignment.rightHandSide())
+ );
+ m_interface->addAssertion(newValue(varDecl) == store);
+ }
+ else if (dynamic_cast<IndexAccess const*>(&indexAccess.baseExpression()))
+ m_errorReporter.warning(
+ indexAccess.location(),
+ "Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays."
+ );
+ else
+ m_errorReporter.warning(
+ _assignment.location(),
+ "Assertion checker does not yet implement this expression."
+ );
+}
+
+void SMTChecker::defineGlobalVariable(string const& _name, Expression const& _expr, bool _increaseIndex)
+{
+ if (!knownGlobalSymbol(_name))
{
auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface);
- m_specialVariables.emplace(_name, result.second);
+ m_globalContext.emplace(_name, result.second);
setUnknownValue(*result.second);
if (result.first)
m_errorReporter.warning(
_expr.location(),
- "Assertion checker does not yet support this special variable."
+ "Assertion checker does not yet support this global variable."
);
}
else if (_increaseIndex)
- m_specialVariables.at(_name)->increaseIndex();
+ m_globalContext.at(_name)->increaseIndex();
// The default behavior is not to increase the index since
- // most of the special values stay the same throughout a tx.
- defineExpr(_expr, m_specialVariables.at(_name)->currentValue());
+ // most of the global values stay the same throughout a tx.
+ if (isSupportedType(_expr.annotation().type->category()))
+ defineExpr(_expr, m_globalContext.at(_name)->currentValue());
}
-void SMTChecker::defineUninterpretedFunction(string const& _name, smt::SortPointer _sort)
+void SMTChecker::defineGlobalFunction(string const& _name, Expression const& _expr)
{
- if (!m_uninterpretedFunctions.count(_name))
- m_uninterpretedFunctions.emplace(_name, m_interface->newVariable(_name, _sort));
+ if (!knownGlobalSymbol(_name))
+ {
+ auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface);
+ m_globalContext.emplace(_name, result.second);
+ if (result.first)
+ m_errorReporter.warning(
+ _expr.location(),
+ "Assertion checker does not yet support the type of this function."
+ );
+ }
}
void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
@@ -753,6 +881,8 @@ void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expressio
checkUnderOverflow(_value, *intType, _location);
else if (dynamic_cast<AddressType const*>(type.get()))
checkUnderOverflow(_value, IntegerType(160), _location);
+ else if (dynamic_cast<MappingType const*>(type.get()))
+ arrayAssignment();
m_interface->addAssertion(newValue(_variable) == _value);
}
@@ -797,18 +927,31 @@ void SMTChecker::checkCondition(
}
for (auto const& var: m_variables)
{
- expressionsToEvaluate.emplace_back(currentValue(*var.first));
- expressionNames.push_back(var.first->name());
+ if (var.first->type()->isValueType())
+ {
+ expressionsToEvaluate.emplace_back(currentValue(*var.first));
+ expressionNames.push_back(var.first->name());
+ }
}
- for (auto const& var: m_specialVariables)
+ for (auto const& var: m_globalContext)
{
- expressionsToEvaluate.emplace_back(var.second->currentValue());
- expressionNames.push_back(var.first);
+ auto const& type = var.second->type();
+ if (
+ type->isValueType() &&
+ smtKind(type->category()) != smt::Kind::Function
+ )
+ {
+ expressionsToEvaluate.emplace_back(var.second->currentValue());
+ expressionNames.push_back(var.first);
+ }
}
for (auto const& uf: m_uninterpretedTerms)
{
- expressionsToEvaluate.emplace_back(expr(*uf));
- expressionNames.push_back(m_scanner->sourceAt(uf->location()));
+ if (uf->annotation().type->isValueType())
+ {
+ expressionsToEvaluate.emplace_back(expr(*uf));
+ expressionNames.push_back(m_scanner->sourceAt(uf->location()));
+ }
}
}
smt::CheckResult result;
@@ -820,6 +963,13 @@ void SMTChecker::checkCondition(
loopComment =
"\nNote that some information is erased after the execution of loops.\n"
"You can re-introduce information using require().";
+ if (m_arrayAssignmentHappened)
+ loopComment +=
+ "\nNote that array aliasing is not supported,"
+ " therefore all mapping information is erased after"
+ " a mapping local variable/parameter is assigned.\n"
+ "You can re-introduce information using require().";
+
switch (result)
{
case smt::CheckResult::SATISFIABLE:
@@ -838,19 +988,19 @@ void SMTChecker::checkCondition(
for (auto const& eval: sortedModel)
modelMessage << " " << eval.first << " = " << eval.second << "\n";
- m_errorReporter.warning(_location, message.str() + loopComment, SecondarySourceLocation().append(modelMessage.str(), SourceLocation()));
+ m_errorReporter.warning(_location, message.str(), SecondarySourceLocation().append(modelMessage.str(), SourceLocation()).append(loopComment, SourceLocation()));
}
else
{
message << ".";
- m_errorReporter.warning(_location, message.str() + loopComment);
+ m_errorReporter.warning(_location, message.str(), SecondarySourceLocation().append(loopComment, SourceLocation()));
}
break;
}
case smt::CheckResult::UNSATISFIABLE:
break;
case smt::CheckResult::UNKNOWN:
- m_errorReporter.warning(_location, _description + " might happen here." + loopComment);
+ m_errorReporter.warning(_location, _description + " might happen here.", SecondarySourceLocation().append(loopComment, SourceLocation()));
break;
case smt::CheckResult::CONFLICTING:
m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
@@ -933,7 +1083,7 @@ SMTChecker::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _exp
try
{
// Parse and re-format nicely
- value = formatNumber(bigint(value));
+ value = formatNumberReadable(bigint(value));
}
catch (...) { }
}
@@ -952,7 +1102,11 @@ void SMTChecker::initializeFunctionCallParameters(FunctionDefinition const& _fun
solAssert(funParams.size() == _callArgs.size(), "");
for (unsigned i = 0; i < funParams.size(); ++i)
if (createVariable(*funParams[i]))
+ {
m_interface->addAssertion(_callArgs[i] == newValue(*funParams[i]));
+ if (funParams[i]->annotation().type->category() == Type::Category::Mapping)
+ m_arrayAssignmentHappened = true;
+ }
for (auto const& variable: _function.localVariables())
if (createVariable(*variable))
@@ -1115,9 +1269,9 @@ bool SMTChecker::knownExpr(Expression const& _e) const
return m_expressions.count(&_e);
}
-bool SMTChecker::knownSpecialVariable(string const& _var) const
+bool SMTChecker::knownGlobalSymbol(string const& _var) const
{
- return m_specialVariables.count(_var);
+ return m_globalContext.count(_var);
}
void SMTChecker::createExpr(Expression const& _e)
@@ -1140,6 +1294,7 @@ void SMTChecker::createExpr(Expression const& _e)
void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value)
{
createExpr(_e);
+ solAssert(isSupportedType(*_e.annotation().type), "Equality operator applied to type that is not fully supported");
m_interface->addAssertion(expr(_e) == _value);
}
diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h
index 34724848..f14d2ac0 100644
--- a/libsolidity/formal/SMTChecker.h
+++ b/libsolidity/formal/SMTChecker.h
@@ -22,13 +22,11 @@
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/ast/ASTVisitor.h>
-
#include <libsolidity/interface/ReadFile.h>
-
#include <liblangutil/Scanner.h>
-#include <unordered_map>
#include <string>
+#include <unordered_map>
#include <vector>
namespace langutil
@@ -79,21 +77,32 @@ private:
void endVisit(Literal const& _node) override;
void endVisit(Return const& _node) override;
bool visit(MemberAccess const& _node) override;
+ void endVisit(IndexAccess const& _node) override;
void arithmeticOperation(BinaryOperation const& _op);
void compareOperation(BinaryOperation const& _op);
void booleanOperation(BinaryOperation const& _op);
- void visitAssert(FunctionCall const&);
- void visitRequire(FunctionCall const&);
- void visitGasLeft(FunctionCall const&);
- void visitBlockHash(FunctionCall const&);
+ void visitAssert(FunctionCall const& _funCall);
+ void visitRequire(FunctionCall const& _funCall);
+ void visitGasLeft(FunctionCall const& _funCall);
/// Visits the FunctionDefinition of the called function
/// if available and inlines the return value.
- void inlineFunctionCall(FunctionCall const&);
-
- void defineSpecialVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false);
- void defineUninterpretedFunction(std::string const& _name, smt::SortPointer _sort);
+ void inlineFunctionCall(FunctionCall const& _funCall);
+ /// Creates an uninterpreted function call.
+ void abstractFunctionCall(FunctionCall const& _funCall);
+ void visitFunctionIdentifier(Identifier const& _identifier);
+
+ void defineGlobalVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false);
+ void defineGlobalFunction(std::string const& _name, Expression const& _expr);
+ /// Handles the side effects of assignment
+ /// to variable of some SMT array type
+ /// while aliasing is not supported.
+ void arrayAssignment();
+ /// Handles assignment to SMT array index.
+ void arrayIndexAssignment(Assignment const& _assignment);
+ /// Erases information about SMT arrays.
+ void eraseArrayKnowledge();
/// Division expression in the given type. Requires special treatment because
/// of rounding for signed division.
@@ -176,8 +185,8 @@ private:
/// Creates the expression and sets its value.
void defineExpr(Expression const& _e, smt::Expression _value);
- /// Checks if special variable was seen.
- bool knownSpecialVariable(std::string const& _var) const;
+ /// Checks if special variable or function was seen.
+ bool knownGlobalSymbol(std::string const& _var) const;
/// Adds a new path condition
void pushPathCondition(smt::Expression const& _e);
@@ -201,16 +210,16 @@ private:
std::shared_ptr<smt::SolverInterface> m_interface;
std::shared_ptr<VariableUsage> m_variableUsage;
bool m_loopExecutionHappened = false;
+ bool m_arrayAssignmentHappened = false;
/// An Expression may have multiple smt::Expression due to
/// repeated calls to the same function.
std::unordered_map<Expression const*, std::shared_ptr<SymbolicVariable>> m_expressions;
std::unordered_map<VariableDeclaration const*, std::shared_ptr<SymbolicVariable>> m_variables;
- std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_specialVariables;
- /// Stores the declaration of an Uninterpreted Function.
- std::unordered_map<std::string, smt::Expression> m_uninterpretedFunctions;
+ std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_globalContext;
/// Stores the instances of an Uninterpreted Function applied to arguments.
+ /// These may be direct application of UFs or Array index access.
/// Used to retrieve models.
- std::vector<Expression const*> m_uninterpretedTerms;
+ std::set<Expression const*> m_uninterpretedTerms;
std::vector<smt::Expression> m_pathConditions;
langutil::ErrorReporter& m_errorReporter;
std::shared_ptr<langutil::Scanner> m_scanner;
diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp
index 3cfa01b1..a23dbe55 100644
--- a/libsolidity/formal/SMTLib2Interface.cpp
+++ b/libsolidity/formal/SMTLib2Interface.cpp
@@ -17,22 +17,20 @@
#include <libsolidity/formal/SMTLib2Interface.h>
-#include <liblangutil/Exceptions.h>
#include <libsolidity/interface/ReadFile.h>
-
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Keccak256.h>
-#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
+#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
-#include <cstdio>
+#include <array>
#include <fstream>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
-#include <array>
using namespace std;
using namespace dev;
diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h
index 55fc4096..d0bf4702 100644
--- a/libsolidity/formal/SMTLib2Interface.h
+++ b/libsolidity/formal/SMTLib2Interface.h
@@ -19,20 +19,17 @@
#include <libsolidity/formal/SolverInterface.h>
-#include <liblangutil/Exceptions.h>
#include <libsolidity/interface/ReadFile.h>
-
-#include <libdevcore/FixedHash.h>
-
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Common.h>
+#include <libdevcore/FixedHash.h>
#include <boost/noncopyable.hpp>
-
+#include <cstdio>
#include <map>
+#include <set>
#include <string>
#include <vector>
-#include <cstdio>
-#include <set>
namespace dev
{
diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h
index 7f5ba37e..8c38bd2e 100644
--- a/libsolidity/formal/SMTPortfolio.h
+++ b/libsolidity/formal/SMTPortfolio.h
@@ -19,13 +19,10 @@
#include <libsolidity/formal/SolverInterface.h>
-
#include <libsolidity/interface/ReadFile.h>
-
#include <libdevcore/FixedHash.h>
#include <boost/noncopyable.hpp>
-
#include <map>
#include <vector>
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index 4a4b3fb1..6e0b17ac 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -17,18 +17,16 @@
#pragma once
-#include <liblangutil/Exceptions.h>
#include <libsolidity/interface/ReadFile.h>
-
+#include <liblangutil/Exceptions.h>
#include <libdevcore/Common.h>
#include <libdevcore/Exceptions.h>
#include <boost/noncopyable.hpp>
-
+#include <cstdio>
#include <map>
#include <string>
#include <vector>
-#include <cstdio>
namespace dev
{
@@ -80,6 +78,8 @@ struct FunctionSort: public Sort
[&](SortPointer _a, SortPointer _b) { return *_a == *_b; }
))
return false;
+ solAssert(codomain, "");
+ solAssert(_otherFunction->codomain, "");
return *codomain == *_otherFunction->codomain;
}
@@ -99,6 +99,10 @@ struct ArraySort: public Sort
return false;
auto _otherArray = dynamic_cast<ArraySort const*>(&_other);
solAssert(_otherArray, "");
+ solAssert(_otherArray->domain, "");
+ solAssert(_otherArray->range, "");
+ solAssert(domain, "");
+ solAssert(range, "");
return *domain == *_otherArray->domain && *range == *_otherArray->range;
}
@@ -161,8 +165,9 @@ public:
static Expression select(Expression _array, Expression _index)
{
solAssert(_array.sort->kind == Kind::Array, "");
- auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get());
+ std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
solAssert(arraySort, "");
+ solAssert(_index.sort, "");
solAssert(*arraySort->domain == *_index.sort, "");
return Expression(
"select",
@@ -176,14 +181,16 @@ public:
static Expression store(Expression _array, Expression _index, Expression _element)
{
solAssert(_array.sort->kind == Kind::Array, "");
- auto const& arraySort = dynamic_cast<ArraySort const*>(_array.sort.get());
+ std::shared_ptr<ArraySort> arraySort = std::dynamic_pointer_cast<ArraySort>(_array.sort);
solAssert(arraySort, "");
+ solAssert(_index.sort, "");
+ solAssert(_element.sort, "");
solAssert(*arraySort->domain == *_index.sort, "");
solAssert(*arraySort->range == *_element.sort, "");
return Expression(
"store",
std::vector<Expression>{std::move(_array), std::move(_index), std::move(_element)},
- _array.sort
+ arraySort
);
}
diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp
index c297c807..269bff73 100644
--- a/libsolidity/formal/SymbolicTypes.cpp
+++ b/libsolidity/formal/SymbolicTypes.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/SymbolicTypes.h>
#include <libsolidity/ast/Types.h>
-
#include <memory>
using namespace std;
@@ -38,17 +37,33 @@ smt::SortPointer dev::solidity::smtSort(Type const& _type)
solAssert(fType, "");
vector<smt::SortPointer> parameterSorts = smtSort(fType->parameterTypes());
auto returnTypes = fType->returnParameterTypes();
- // TODO remove this when we support tuples.
- solAssert(returnTypes.size() == 1, "");
- smt::SortPointer returnSort = smtSort(*returnTypes.at(0));
+ smt::SortPointer returnSort;
+ // TODO change this when we support tuples.
+ if (returnTypes.size() == 0)
+ // We cannot declare functions without a return sort, so we use the smallest.
+ returnSort = make_shared<smt::Sort>(smt::Kind::Bool);
+ else if (returnTypes.size() > 1)
+ // Abstract sort.
+ returnSort = make_shared<smt::Sort>(smt::Kind::Int);
+ else
+ returnSort = smtSort(*returnTypes.at(0));
return make_shared<smt::FunctionSort>(parameterSorts, returnSort);
}
case smt::Kind::Array:
{
- solUnimplementedAssert(false, "Invalid type");
+ if (isMapping(_type.category()))
+ {
+ auto mapType = dynamic_cast<MappingType const*>(&_type);
+ solAssert(mapType, "");
+ return make_shared<smt::ArraySort>(smtSort(*mapType->keyType()), smtSort(*mapType->valueType()));
+ }
+ // TODO Solidity array
+ return make_shared<smt::Sort>(smt::Kind::Int);
}
+ default:
+ // Abstract case.
+ return make_shared<smt::Sort>(smt::Kind::Int);
}
- solAssert(false, "Invalid type");
}
vector<smt::SortPointer> dev::solidity::smtSort(vector<TypePointer> const& _types)
@@ -65,13 +80,24 @@ smt::Kind dev::solidity::smtKind(Type::Category _category)
return smt::Kind::Int;
else if (isBool(_category))
return smt::Kind::Bool;
- solAssert(false, "Invalid type");
+ else if (isFunction(_category))
+ return smt::Kind::Function;
+ else if (isMapping(_category))
+ return smt::Kind::Array;
+ // Abstract case.
+ return smt::Kind::Int;
}
bool dev::solidity::isSupportedType(Type::Category _category)
{
return isNumber(_category) ||
isBool(_category) ||
+ isMapping(_category);
+}
+
+bool dev::solidity::isSupportedTypeDeclaration(Type::Category _category)
+{
+ return isSupportedType(_category) ||
isFunction(_category);
}
@@ -84,7 +110,7 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
bool abstract = false;
shared_ptr<SymbolicVariable> var;
TypePointer type = _type.shared_from_this();
- if (!isSupportedType(_type))
+ if (!isSupportedTypeDeclaration(_type))
{
abstract = true;
var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver);
@@ -92,7 +118,7 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
else if (isBool(_type.category()))
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _solver);
else if (isFunction(_type.category()))
- var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver);
+ var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _solver);
else if (isInteger(_type.category()))
var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver);
else if (isFixedBytes(_type.category()))
@@ -112,6 +138,8 @@ pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable(
else
var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver);
}
+ else if (isMapping(_type.category()))
+ var = make_shared<SymbolicMappingVariable>(type, _uniqueName, _solver);
else
solAssert(false, "");
return make_pair(abstract, var);
@@ -122,6 +150,11 @@ bool dev::solidity::isSupportedType(Type const& _type)
return isSupportedType(_type.category());
}
+bool dev::solidity::isSupportedTypeDeclaration(Type const& _type)
+{
+ return isSupportedTypeDeclaration(_type.category());
+}
+
bool dev::solidity::isInteger(Type::Category _category)
{
return _category == Type::Category::Integer;
@@ -160,6 +193,11 @@ bool dev::solidity::isFunction(Type::Category _category)
return _category == Type::Category::Function;
}
+bool dev::solidity::isMapping(Type::Category _category)
+{
+ return _category == Type::Category::Mapping;
+}
+
smt::Expression dev::solidity::minValue(IntegerType const& _type)
{
return smt::Expression(_type.minValue());
diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h
index 984653b3..35c7bb8d 100644
--- a/libsolidity/formal/SymbolicTypes.h
+++ b/libsolidity/formal/SymbolicTypes.h
@@ -19,7 +19,6 @@
#include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/formal/SymbolicVariables.h>
-
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
@@ -34,10 +33,12 @@ std::vector<smt::SortPointer> smtSort(std::vector<TypePointer> const& _types);
/// Returns the SMT kind that models the Solidity type type category _category.
smt::Kind smtKind(Type::Category _category);
-/// So far int, bool and address are supported.
-/// Returns true if type is supported.
+/// Returns true if type is fully supported (declaration and operations).
bool isSupportedType(Type::Category _category);
bool isSupportedType(Type const& _type);
+/// Returns true if type is partially supported (declaration).
+bool isSupportedTypeDeclaration(Type::Category _category);
+bool isSupportedTypeDeclaration(Type const& _type);
bool isInteger(Type::Category _category);
bool isRational(Type::Category _category);
@@ -46,6 +47,7 @@ bool isAddress(Type::Category _category);
bool isNumber(Type::Category _category);
bool isBool(Type::Category _category);
bool isFunction(Type::Category _category);
+bool isMapping(Type::Category _category);
/// Returns a new symbolic variable, according to _type.
/// Also returns whether the type is abstract or not,
diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp
index efaeb97a..c4fc81da 100644
--- a/libsolidity/formal/SymbolicVariables.cpp
+++ b/libsolidity/formal/SymbolicVariables.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/SymbolicVariables.h>
#include <libsolidity/formal/SymbolicTypes.h>
-
#include <libsolidity/ast/AST.h>
using namespace std;
@@ -37,16 +36,32 @@ SymbolicVariable::SymbolicVariable(
{
}
+smt::Expression SymbolicVariable::currentValue() const
+{
+ return valueAtIndex(m_ssa->index());
+}
+
string SymbolicVariable::currentName() const
{
return uniqueSymbol(m_ssa->index());
}
+smt::Expression SymbolicVariable::valueAtIndex(int _index) const
+{
+ return m_interface.newVariable(uniqueSymbol(_index), smtSort(*m_type));
+}
+
string SymbolicVariable::uniqueSymbol(unsigned _index) const
{
return m_uniqueName + "_" + to_string(_index);
}
+smt::Expression SymbolicVariable::increaseIndex()
+{
+ ++(*m_ssa);
+ return currentValue();
+}
+
SymbolicBoolVariable::SymbolicBoolVariable(
TypePointer _type,
string const& _uniqueName,
@@ -57,11 +72,6 @@ SymbolicBoolVariable::SymbolicBoolVariable(
solAssert(m_type->category() == Type::Category::Bool, "");
}
-smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const
-{
- return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Bool));
-}
-
SymbolicIntVariable::SymbolicIntVariable(
TypePointer _type,
string const& _uniqueName,
@@ -72,11 +82,6 @@ SymbolicIntVariable::SymbolicIntVariable(
solAssert(isNumber(m_type->category()), "");
}
-smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const
-{
- return m_interface.newVariable(uniqueSymbol(_index), make_shared<smt::Sort>(smt::Kind::Int));
-}
-
SymbolicAddressVariable::SymbolicAddressVariable(
string const& _uniqueName,
smt::SolverInterface& _interface
@@ -93,3 +98,41 @@ SymbolicFixedBytesVariable::SymbolicFixedBytesVariable(
SymbolicIntVariable(make_shared<IntegerType>(_numBytes * 8), _uniqueName, _interface)
{
}
+
+SymbolicFunctionVariable::SymbolicFunctionVariable(
+ TypePointer _type,
+ string const& _uniqueName,
+ smt::SolverInterface&_interface
+):
+ SymbolicVariable(move(_type), _uniqueName, _interface),
+ m_declaration(m_interface.newVariable(currentName(), smtSort(*m_type)))
+{
+ solAssert(m_type->category() == Type::Category::Function, "");
+}
+
+void SymbolicFunctionVariable::resetDeclaration()
+{
+ m_declaration = m_interface.newVariable(currentName(), smtSort(*m_type));
+}
+
+smt::Expression SymbolicFunctionVariable::increaseIndex()
+{
+ ++(*m_ssa);
+ resetDeclaration();
+ return currentValue();
+}
+
+smt::Expression SymbolicFunctionVariable::operator()(vector<smt::Expression> _arguments) const
+{
+ return m_declaration(_arguments);
+}
+
+SymbolicMappingVariable::SymbolicMappingVariable(
+ TypePointer _type,
+ string const& _uniqueName,
+ smt::SolverInterface& _interface
+):
+ SymbolicVariable(move(_type), _uniqueName, _interface)
+{
+ solAssert(isMapping(m_type->category()), "");
+}
diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h
index fcf32760..86abf4f1 100644
--- a/libsolidity/formal/SymbolicVariables.h
+++ b/libsolidity/formal/SymbolicVariables.h
@@ -17,12 +17,9 @@
#pragma once
-#include <libsolidity/formal/SSAVariable.h>
-
#include <libsolidity/formal/SolverInterface.h>
-
+#include <libsolidity/formal/SSAVariable.h>
#include <libsolidity/ast/Types.h>
-
#include <memory>
namespace dev
@@ -46,19 +43,13 @@ public:
virtual ~SymbolicVariable() = default;
- smt::Expression currentValue() const
- {
- return valueAtIndex(m_ssa->index());
- }
-
+ smt::Expression currentValue() const;
std::string currentName() const;
-
- virtual smt::Expression valueAtIndex(int _index) const = 0;
-
- smt::Expression increaseIndex()
+ virtual smt::Expression valueAtIndex(int _index) const;
+ virtual smt::Expression increaseIndex();
+ virtual smt::Expression operator()(std::vector<smt::Expression> /*_arguments*/) const
{
- ++(*m_ssa);
- return currentValue();
+ solAssert(false, "Function application to non-function.");
}
unsigned index() const { return m_ssa->index(); }
@@ -86,9 +77,6 @@ public:
std::string const& _uniqueName,
smt::SolverInterface& _interface
);
-
-protected:
- smt::Expression valueAtIndex(int _index) const;
};
/**
@@ -102,9 +90,6 @@ public:
std::string const& _uniqueName,
smt::SolverInterface& _interface
);
-
-protected:
- smt::Expression valueAtIndex(int _index) const;
};
/**
@@ -132,5 +117,41 @@ public:
);
};
+/**
+ * Specialization of SymbolicVariable for FunctionType
+ */
+class SymbolicFunctionVariable: public SymbolicVariable
+{
+public:
+ SymbolicFunctionVariable(
+ TypePointer _type,
+ std::string const& _uniqueName,
+ smt::SolverInterface& _interface
+ );
+
+ smt::Expression increaseIndex();
+ smt::Expression operator()(std::vector<smt::Expression> _arguments) const;
+
+private:
+ /// Creates a new function declaration.
+ void resetDeclaration();
+
+ /// Stores the current function declaration.
+ smt::Expression m_declaration;
+};
+
+/**
+ * Specialization of SymbolicVariable for Mapping
+ */
+class SymbolicMappingVariable: public SymbolicVariable
+{
+public:
+ SymbolicMappingVariable(
+ TypePointer _type,
+ std::string const& _uniqueName,
+ smt::SolverInterface& _interface
+ );
+};
+
}
}
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
index cb01dc61..4cbc3271 100644
--- a/libsolidity/formal/Z3Interface.cpp
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -18,7 +18,6 @@
#include <libsolidity/formal/Z3Interface.h>
#include <liblangutil/Exceptions.h>
-
#include <libdevcore/CommonIO.h>
using namespace std;
diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h
index 86e1badd..ee4d1551 100644
--- a/libsolidity/formal/Z3Interface.h
+++ b/libsolidity/formal/Z3Interface.h
@@ -18,9 +18,7 @@
#pragma once
#include <libsolidity/formal/SolverInterface.h>
-
#include <boost/noncopyable.hpp>
-
#include <z3++.h>
namespace dev
diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp
index aefb34af..0d27109e 100644
--- a/libsolidity/interface/ABI.cpp
+++ b/libsolidity/interface/ABI.cpp
@@ -19,6 +19,7 @@
*/
#include <libsolidity/interface/ABI.h>
+
#include <libsolidity/ast/AST.h>
using namespace std;
diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h
index db70729d..082f3900 100644
--- a/libsolidity/interface/ABI.h
+++ b/libsolidity/interface/ABI.h
@@ -20,9 +20,9 @@
#pragma once
-#include <string>
-#include <memory>
#include <json/json.h>
+#include <memory>
+#include <string>
namespace dev
{
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index f5eb7e41..69bceefc 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -22,18 +22,19 @@
#include <libsolidity/interface/AssemblyStack.h>
+#include <libsolidity/codegen/AsmCodeGen.h>
+#include <libevmasm/Assembly.h>
#include <liblangutil/Scanner.h>
-#include <libyul/AsmPrinter.h>
-#include <libyul/AsmParser.h>
+
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
-#include <libyul/AsmCodeGen.h>
-#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/AsmParser.h>
+#include <libyul/AsmPrinter.h>
#include <libyul/backends/evm/EVMAssembly.h>
+#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/backends/evm/EVMDialect.h>
+#include <libyul/backends/evm/EVMObjectCompiler.h>
#include <libyul/ObjectParser.h>
-
-#include <libevmasm/Assembly.h>
-
#include <libyul/optimiser/Suite.h>
using namespace std;
@@ -43,19 +44,19 @@ using namespace dev::solidity;
namespace
{
-yul::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language)
+shared_ptr<yul::Dialect> languageToDialect(AssemblyStack::Language _language)
{
switch (_language)
{
case AssemblyStack::Language::Assembly:
- return yul::AsmFlavour::Loose;
+ return yul::EVMDialect::looseAssemblyForEVM();
case AssemblyStack::Language::StrictAssembly:
- return yul::AsmFlavour::Strict;
+ return yul::EVMDialect::strictAssemblyForEVMObjects();
case AssemblyStack::Language::Yul:
- return yul::AsmFlavour::Yul;
+ return yul::Dialect::yul();
}
solAssert(false, "");
- return yul::AsmFlavour::Yul;
+ return yul::Dialect::yul();
}
}
@@ -72,7 +73,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
m_errors.clear();
m_analysisSuccessful = false;
m_scanner = make_shared<Scanner>(CharStream(_source, _sourceName));
- m_parserResult = yul::ObjectParser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false);
+ m_parserResult = yul::ObjectParser(m_errorReporter, languageToDialect(m_language)).parse(m_scanner, false);
if (!m_errorReporter.errors().empty())
return false;
solAssert(m_parserResult, "");
@@ -84,21 +85,59 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
void AssemblyStack::optimize()
{
solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly.");
- yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo);
+ solAssert(m_analysisSuccessful, "Analysis was not successful.");
+ m_analysisSuccessful = false;
+ optimize(*m_parserResult);
solAssert(analyzeParsed(), "Invalid source code after optimization.");
}
bool AssemblyStack::analyzeParsed()
{
solAssert(m_parserResult, "");
- solAssert(m_parserResult->code, "");
- m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>();
- yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language));
- m_analysisSuccessful = analyzer.analyze(*m_parserResult->code);
+ m_analysisSuccessful = analyzeParsed(*m_parserResult);
return m_analysisSuccessful;
}
-MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
+bool AssemblyStack::analyzeParsed(yul::Object& _object)
+{
+ solAssert(_object.code, "");
+ _object.analysisInfo = make_shared<yul::AsmAnalysisInfo>();
+ yul::AsmAnalyzer analyzer(*_object.analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToDialect(m_language));
+ bool success = analyzer.analyze(*_object.code);
+ for (auto& subNode: _object.subObjects)
+ if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
+ if (!analyzeParsed(*subObject))
+ success = false;
+ return success;
+}
+
+void AssemblyStack::compileEVM(yul::AbstractAssembly& _assembly, bool _evm15, bool _optimize) const
+{
+ shared_ptr<yul::EVMDialect> dialect;
+
+ if (m_language == Language::Assembly)
+ dialect = yul::EVMDialect::looseAssemblyForEVM();
+ else if (m_language == AssemblyStack::Language::StrictAssembly)
+ dialect = yul::EVMDialect::strictAssemblyForEVMObjects();
+ else if (m_language == AssemblyStack::Language::Yul)
+ dialect = yul::EVMDialect::yulForEVM();
+ else
+ solAssert(false, "Invalid language.");
+
+ yul::EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _evm15, _optimize);
+}
+
+void AssemblyStack::optimize(yul::Object& _object)
+{
+ solAssert(_object.code, "");
+ solAssert(_object.analysisInfo, "");
+ for (auto& subNode: _object.subObjects)
+ if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
+ optimize(*subObject);
+ yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo);
+}
+
+MachineAssemblyObject AssemblyStack::assemble(Machine _machine, bool _optimize) const
{
solAssert(m_analysisSuccessful, "");
solAssert(m_parserResult, "");
@@ -111,7 +150,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
MachineAssemblyObject object;
eth::Assembly assembly;
- yul::CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly);
+ EthAssemblyAdapter adapter(assembly);
+ compileEVM(adapter, false, _optimize);
object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
object.assembly = assembly.assemblyString();
return object;
@@ -120,7 +160,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
MachineAssemblyObject object;
yul::EVMAssembly assembly(true);
- yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code);
+ compileEVM(assembly, true, _optimize);
object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
/// TODO: fill out text representation
return object;
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index 0d04ffec..01db6b61 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -29,13 +29,17 @@
#include <libevmasm/LinkerObject.h>
-#include <string>
#include <memory>
+#include <string>
namespace langutil
{
class Scanner;
}
+namespace yul
+{
+class AbstractAssembly;
+}
namespace dev
{
@@ -73,7 +77,8 @@ public:
void optimize();
/// Run the assembly step (should only be called after parseAndAnalyze).
- MachineAssemblyObject assemble(Machine _machine) const;
+ /// @param _optimize does not run the optimizer but performs optimized code generation.
+ MachineAssemblyObject assemble(Machine _machine, bool _optimize = false) const;
/// @returns the errors generated during parsing, analysis (and potentially assembly).
langutil::ErrorList const& errors() const { return m_errors; }
@@ -83,6 +88,11 @@ public:
private:
bool analyzeParsed();
+ bool analyzeParsed(yul::Object& _object);
+
+ void compileEVM(yul::AbstractAssembly& _assembly, bool _evm15, bool _optimize) const;
+
+ void optimize(yul::Object& _object);
Language m_language = Language::Assembly;
EVMVersion m_evmVersion;
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 610caea1..f9d889e7 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -24,26 +24,27 @@
#include <libsolidity/interface/CompilerStack.h>
-#include <libsolidity/interface/Version.h>
-#include <libsolidity/analysis/SemVerHandler.h>
-#include <libsolidity/ast/AST.h>
-#include <libsolidity/parsing/Parser.h>
-#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <libsolidity/analysis/ControlFlowGraph.h>
+#include <libsolidity/analysis/ContractLevelChecker.h>
+#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
-#include <libsolidity/analysis/TypeChecker.h>
-#include <libsolidity/analysis/DocStringAnalyser.h>
-#include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/PostTypeChecker.h>
+#include <libsolidity/analysis/SemVerHandler.h>
+#include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/SyntaxChecker.h>
+#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/analysis/ViewPureChecker.h>
+
+#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/formal/SMTChecker.h>
#include <libsolidity/interface/ABI.h>
#include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h>
+#include <libsolidity/interface/Version.h>
+#include <libsolidity/parsing/Parser.h>
#include <libyul/YulString.h>
@@ -388,18 +389,27 @@ string const CompilerStack::lastContractName() const
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
return currentContract.compiler ? &contract(_contractName).compiler->assemblyItems() : nullptr;
}
eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
}
string const* CompilerStack::sourceMapping(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& c = contract(_contractName);
if (!c.sourceMapping)
{
@@ -411,6 +421,9 @@ string const* CompilerStack::sourceMapping(string const& _contractName) const
string const* CompilerStack::runtimeSourceMapping(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& c = contract(_contractName);
if (!c.runtimeSourceMapping)
{
@@ -446,17 +459,26 @@ std::string const CompilerStack::filesystemFriendlyName(string const& _contractN
eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
return contract(_contractName).object;
}
eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
return contract(_contractName).runtimeObject;
}
/// FIXME: cache this string
string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
if (currentContract.compiler)
return currentContract.compiler->assemblyString(_sourceCodes);
@@ -467,6 +489,9 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap _sou
/// FIXME: cache the JSON
Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap _sourceCodes) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
Contract const& currentContract = contract(_contractName);
if (currentContract.compiler)
return currentContract.compiler->assemblyJSON(_sourceCodes);
@@ -493,13 +518,16 @@ map<string, unsigned> CompilerStack::sourceIndices() const
Json::Value const& CompilerStack::contractABI(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
return contractABI(contract(_contractName));
}
Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
@@ -512,13 +540,16 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
return natspecUser(contract(_contractName));
}
Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
@@ -531,13 +562,16 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const
Json::Value const& CompilerStack::natspecDev(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
return natspecDev(contract(_contractName));
}
Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
{
if (m_stackState < AnalysisSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
@@ -550,9 +584,12 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const
Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const
{
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
+
Json::Value methodIdentifiers(Json::objectValue);
for (auto const& it: contractDefinition(_contractName).interfaceFunctions())
- methodIdentifiers[it.second->externalSignature()] = toHex(it.first.ref());
+ methodIdentifiers[it.second->externalSignature()] = it.first.hex();
return methodIdentifiers;
}
@@ -582,8 +619,8 @@ SourceUnit const& CompilerStack::ast(string const& _sourceName) const
ContractDefinition const& CompilerStack::contractDefinition(string const& _contractName) const
{
- if (m_stackState != CompilationSuccessful)
- BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+ if (m_stackState < AnalysisSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
return *contract(_contractName).contract;
}
@@ -593,6 +630,9 @@ size_t CompilerStack::functionEntryPoint(
FunctionDefinition const& _function
) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
shared_ptr<Compiler> const& compiler = contract(_contractName).compiler;
if (!compiler)
return 0;
@@ -618,6 +658,22 @@ tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocati
return make_tuple(++startLine, ++startColumn, ++endLine, ++endColumn);
}
+
+h256 const& CompilerStack::Source::keccak256() const
+{
+ if (keccak256HashCached == h256{})
+ keccak256HashCached = dev::keccak256(scanner->source());
+ return keccak256HashCached;
+}
+
+h256 const& CompilerStack::Source::swarmHash() const
+{
+ if (swarmHashCached == h256{})
+ swarmHashCached = dev::swarmHash(scanner->source());
+ return swarmHashCached;
+}
+
+
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
{
solAssert(m_stackState < ParsingSuccessful, "");
@@ -859,16 +915,13 @@ string CompilerStack::createMetadata(Contract const& _contract) const
continue;
solAssert(s.second.scanner, "Scanner not available");
- meta["sources"][s.first]["keccak256"] =
- "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes());
+ meta["sources"][s.first]["keccak256"] = "0x" + toHex(s.second.keccak256().asBytes());
if (m_metadataLiteralSources)
meta["sources"][s.first]["content"] = s.second.scanner->source();
else
{
meta["sources"][s.first]["urls"] = Json::arrayValue;
- meta["sources"][s.first]["urls"].append(
- "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes())
- );
+ meta["sources"][s.first]["urls"].append("bzzr://" + toHex(s.second.swarmHash().asBytes()));
}
}
meta["settings"]["optimizer"]["enabled"] = m_optimize;
@@ -895,7 +948,7 @@ string CompilerStack::createMetadata(Contract const& _contract) const
return jsonCompactPrint(meta);
}
-bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode)
+bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimentalMode)
{
bytes cborEncodedHash =
// CBOR-encoding of the key "bzzr0"
@@ -922,6 +975,9 @@ bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode
string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
string ret;
map<string, unsigned> sourceIndicesMap = sourceIndices();
int prevStart = -1;
@@ -1008,6 +1064,9 @@ Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
{
+ if (m_stackState != CompilationSuccessful)
+ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
+
if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
return Json::Value();
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 2c7add3b..81d5009f 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -34,15 +34,14 @@
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
-#include <json/json.h>
-
#include <boost/noncopyable.hpp>
+#include <json/json.h>
+#include <functional>
+#include <memory>
#include <ostream>
#include <string>
-#include <memory>
#include <vector>
-#include <functional>
namespace langutil
{
@@ -262,7 +261,11 @@ private:
std::shared_ptr<langutil::Scanner> scanner;
std::shared_ptr<SourceUnit> ast;
bool isLibrary = false;
- void reset() { scanner.reset(); ast.reset(); }
+ h256 mutable keccak256HashCached;
+ h256 mutable swarmHashCached;
+ void reset() { *this = Source(); }
+ h256 const& keccak256() const;
+ h256 const& swarmHash() const;
};
/// The state per contract. Filled gradually during compilation.
@@ -316,7 +319,7 @@ private:
std::string createMetadata(Contract const& _contract) const;
/// @returns the metadata CBOR for the given serialised metadata JSON.
- static bytes createCBORMetadata(std::string _metadata, bool _experimentalMode);
+ static bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode);
/// @returns the computer source mapping string.
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index de6b2ce5..8ffcf951 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -20,18 +20,21 @@
* Gas consumption estimator working alongside the AST.
*/
-#include "GasEstimator.h"
-#include <map>
-#include <functional>
-#include <memory>
-#include <libdevcore/Keccak256.h>
-#include <libevmasm/ControlFlowGraph.h>
-#include <libevmasm/KnownState.h>
-#include <libevmasm/PathGasMeter.h>
+#include <libsolidity/interface/GasEstimator.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/codegen/CompilerUtils.h>
+#include <libevmasm/ControlFlowGraph.h>
+#include <libevmasm/KnownState.h>
+#include <libevmasm/PathGasMeter.h>
+#include <libdevcore/Keccak256.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+
using namespace std;
using namespace dev;
using namespace dev::eth;
diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h
index 214a3e58..f40cffeb 100644
--- a/libsolidity/interface/GasEstimator.h
+++ b/libsolidity/interface/GasEstimator.h
@@ -24,12 +24,12 @@
#include <liblangutil/EVMVersion.h>
-#include <libevmasm/GasMeter.h>
#include <libevmasm/Assembly.h>
+#include <libevmasm/GasMeter.h>
-#include <vector>
-#include <map>
#include <array>
+#include <map>
+#include <vector>
namespace dev
{
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
index 11dde349..7a89abae 100644
--- a/libsolidity/interface/Natspec.cpp
+++ b/libsolidity/interface/Natspec.cpp
@@ -24,8 +24,9 @@
*/
#include <libsolidity/interface/Natspec.h>
-#include <boost/range/irange.hpp>
+
#include <libsolidity/ast/AST.h>
+#include <boost/range/irange.hpp>
using namespace std;
using namespace dev;
diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h
index 0be4dda2..fbaa6d4d 100644
--- a/libsolidity/interface/Natspec.h
+++ b/libsolidity/interface/Natspec.h
@@ -25,9 +25,9 @@
#pragma once
-#include <string>
-#include <memory>
#include <json/json.h>
+#include <memory>
+#include <string>
namespace dev
{
diff --git a/libsolidity/interface/ReadFile.h b/libsolidity/interface/ReadFile.h
index 7068629d..3b3d747e 100644
--- a/libsolidity/interface/ReadFile.h
+++ b/libsolidity/interface/ReadFile.h
@@ -17,9 +17,9 @@
#pragma once
-#include <string>
-#include <functional>
#include <boost/noncopyable.hpp>
+#include <functional>
+#include <string>
namespace dev
{
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index 0eef50d2..137a4439 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -21,13 +21,17 @@
*/
#include <libsolidity/interface/StandardCompiler.h>
-#include <liblangutil/SourceReferenceFormatter.h>
+
#include <libsolidity/ast/ASTJsonConverter.h>
+#include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>
#include <libdevcore/Keccak256.h>
+#include <boost/algorithm/cxx11/any_of.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/optional.hpp>
+#include <algorithm>
using namespace std;
using namespace dev;
@@ -69,12 +73,11 @@ Json::Value formatErrorWithException(
bool const& _warning,
string const& _type,
string const& _component,
- string const& _message,
- function<Scanner const&(string const&)> const& _scannerFromSourceName
+ string const& _message
)
{
string message;
- string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type, _scannerFromSourceName);
+ string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type);
// NOTE: the below is partially a copy from SourceReferenceFormatter
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
@@ -189,6 +192,31 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
return false;
}
+/// @returns true if any binary was requested, i.e. we actually have to perform compilation.
+bool isBinaryRequested(Json::Value const& _outputSelection)
+{
+ if (!_outputSelection.isObject())
+ return false;
+
+ // This does not inculde "evm.methodIdentifiers" on purpose!
+ static vector<string> const outputsThatRequireBinaries{
+ "*",
+ "metadata", // This is only generated at the end of compilation, but could be generated earlier.
+ "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes",
+ "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences",
+ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap",
+ "evm.bytecode.linkReferences",
+ "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly"
+ };
+
+ for (auto const& fileRequests: _outputSelection)
+ for (auto const& requests: fileRequests)
+ for (auto const& output: outputsThatRequireBinaries)
+ if (isArtifactRequested(requests, output))
+ return true;
+ return false;
+}
+
Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkReferences)
{
Json::Value ret(Json::objectValue);
@@ -226,6 +254,99 @@ Json::Value collectEVMObject(eth::LinkerObject const& _object, string const* _so
return output;
}
+boost::optional<Json::Value> checkKeys(Json::Value const& _input, set<string> const& _keys, string const& _name)
+{
+ if (!!_input && !_input.isObject())
+ return formatFatalError("JSONError", "\"" + _name + "\" must be an object");
+
+ for (auto const& member: _input.getMemberNames())
+ if (!_keys.count(member))
+ return formatFatalError("JSONError", "Unknown key \"" + member + "\"");
+
+ return boost::none;
+}
+
+boost::optional<Json::Value> checkRootKeys(Json::Value const& _input)
+{
+ static set<string> keys{"auxiliaryInput", "language", "settings", "sources"};
+ return checkKeys(_input, keys, "root");
+}
+
+boost::optional<Json::Value> checkSourceKeys(Json::Value const& _input, string const& _name)
+{
+ static set<string> keys{"content", "keccak256", "urls"};
+ return checkKeys(_input, keys, "sources." + _name);
+}
+
+boost::optional<Json::Value> checkAuxiliaryInputKeys(Json::Value const& _input)
+{
+ static set<string> keys{"smtlib2responses"};
+ return checkKeys(_input, keys, "auxiliaryInput");
+}
+
+boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
+{
+ static set<string> keys{"evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"};
+ return checkKeys(_input, keys, "settings");
+}
+
+boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
+{
+ static set<string> keys{"enabled", "runs"};
+ return checkKeys(_input, keys, "settings.optimizer");
+}
+
+boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
+{
+ static set<string> keys{"useLiteralContent"};
+ return checkKeys(_input, keys, "settings.metadata");
+}
+
+boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelection)
+{
+ if (!!_outputSelection && !_outputSelection.isObject())
+ return formatFatalError("JSONError", "\"settings.outputSelection\" must be an object");
+
+ for (auto const& sourceName: _outputSelection.getMemberNames())
+ {
+ auto const& sourceVal = _outputSelection[sourceName];
+
+ if (!sourceVal.isObject())
+ return formatFatalError(
+ "JSONError",
+ "\"settings.outputSelection." + sourceName + "\" must be an object"
+ );
+
+ for (auto const& contractName: sourceVal.getMemberNames())
+ {
+ auto const& contractVal = sourceVal[contractName];
+
+ if (!contractVal.isArray())
+ return formatFatalError(
+ "JSONError",
+ "\"settings.outputSelection." +
+ sourceName +
+ "." +
+ contractName +
+ "\" must be a string array"
+ );
+
+ for (auto const& output: contractVal)
+ if (!output.isString())
+ return formatFatalError(
+ "JSONError",
+ "\"settings.outputSelection." +
+ sourceName +
+ "." +
+ contractName +
+ "\" must be a string array"
+ );
+ }
+ }
+
+ return boost::none;
+}
+
}
Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
@@ -235,6 +356,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (!_input.isObject())
return formatFatalError("JSONError", "Input is not a JSON object.");
+ if (auto result = checkRootKeys(_input))
+ return *result;
+
if (_input["language"] != "Solidity")
return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
@@ -252,8 +376,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
string hash;
- if (!sources[sourceName].isObject())
- return formatFatalError("JSONError", "Source input is not a JSON object.");
+ if (auto result = checkSourceKeys(sources[sourceName], sourceName))
+ return *result;
if (sources[sourceName]["keccak256"].isString())
hash = sources[sourceName]["keccak256"].asString();
@@ -320,10 +444,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
}
Json::Value const& auxInputs = _input["auxiliaryInput"];
+
+ if (auto result = checkAuxiliaryInputKeys(auxInputs))
+ return *result;
+
if (!!auxInputs)
{
Json::Value const& smtlib2Responses = auxInputs["smtlib2responses"];
if (!!smtlib2Responses)
+ {
+ if (!smtlib2Responses.isObject())
+ return formatFatalError("JSONError", "\"auxiliaryInput.smtlib2responses\" must be an object.");
+
for (auto const& hashString: smtlib2Responses.getMemberNames())
{
h256 hash;
@@ -336,12 +468,22 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
return formatFatalError("JSONError", "Invalid hex encoding of SMTLib2 auxiliary input.");
}
+ if (!smtlib2Responses[hashString].isString())
+ return formatFatalError(
+ "JSONError",
+ "\"smtlib2Responses." + hashString + "\" must be a string."
+ );
+
m_compilerStack.addSMTLib2Response(hash, smtlib2Responses[hashString].asString());
}
+ }
}
Json::Value const& settings = _input.get("settings", Json::Value());
+ if (auto result = checkSettingsKeys(settings))
+ return *result;
+
if (settings.isMember("evmVersion"))
{
if (!settings["evmVersion"].isString())
@@ -352,11 +494,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setEVMVersion(*version);
}
+ if (settings.isMember("remappings") && !settings["remappings"].isArray())
+ return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings.");
+
vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
{
if (!remapping.isString())
- return formatFatalError("JSONError", "Remapping entry must be a string.");
+ return formatFatalError("JSONError", "\"settings.remappings\" must be an array of strings");
if (auto r = CompilerStack::parseRemapping(remapping.asString()))
remappings.emplace_back(std::move(*r));
else
@@ -367,6 +512,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (settings.isMember("optimizer"))
{
Json::Value optimizerSettings = settings["optimizer"];
+
+ if (auto result = checkOptimizerKeys(optimizerSettings))
+ return *result;
+
if (optimizerSettings.isMember("enabled"))
{
if (!optimizerSettings["enabled"].isBool())
@@ -428,16 +577,27 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setLibraries(libraries);
Json::Value metadataSettings = settings.get("metadata", Json::Value());
+
+ if (auto result = checkMetadataKeys(metadataSettings))
+ return *result;
+
m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool());
Json::Value outputSelection = settings.get("outputSelection", Json::Value());
+
+ if (auto jsonError = checkOutputSelection(outputSelection))
+ return *jsonError;
+
m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection));
- auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compilerStack.scanner(_sourceName); };
+ bool const binariesRequested = isBinaryRequested(outputSelection);
try
{
- m_compilerStack.compile();
+ if (binariesRequested)
+ m_compilerStack.compile();
+ else
+ m_compilerStack.parseAndAnalyze();
for (auto const& error: m_compilerStack.errors())
{
@@ -448,8 +608,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
err.type() == Error::Type::Warning,
err.typeName(),
"general",
- "",
- scannerFromSourceName
+ ""
));
}
}
@@ -461,8 +620,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
_error.typeName(),
"general",
- "Uncaught error: ",
- scannerFromSourceName
+ "Uncaught error: "
));
}
/// This should not be leaked from compile().
@@ -482,8 +640,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"CompilerError",
"general",
- "Compiler error (" + _exception.lineInfo() + ")",
- scannerFromSourceName
+ "Compiler error (" + _exception.lineInfo() + ")"
));
}
catch (InternalCompilerError const& _exception)
@@ -493,8 +650,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"InternalCompilerError",
"general",
- "Internal compiler error (" + _exception.lineInfo() + ")",
- scannerFromSourceName
+ "Internal compiler error (" + _exception.lineInfo() + ")"
));
}
catch (UnimplementedFeatureError const& _exception)
@@ -504,8 +660,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
false,
"UnimplementedFeatureError",
"general",
- "Unimplemented feature (" + _exception.lineInfo() + ")",
- scannerFromSourceName
+ "Unimplemented feature (" + _exception.lineInfo() + ")"
));
}
catch (Exception const& _exception)
@@ -531,7 +686,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful;
/// Inconsistent state - stop here to receive error reports from users
- if (!compilationSuccess && errors.empty())
+ if (((binariesRequested && !compilationSuccess) || !analysisSuccess) && errors.empty())
return formatFatalError("InternalCompilerError", "No error reported, but compilation failed.");
Json::Value output = Json::objectValue;
@@ -557,7 +712,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
}
Json::Value contractsOutput = Json::objectValue;
- for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector<string>())
+ for (string const& contractName: analysisSuccess ? m_compilerStack.contractNames() : vector<string>())
{
size_t colon = contractName.rfind(':');
solAssert(colon != string::npos, "");
@@ -568,7 +723,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
Json::Value contractData(Json::objectValue);
if (isArtifactRequested(outputSelection, file, name, "abi"))
contractData["abi"] = m_compilerStack.contractABI(contractName);
- if (isArtifactRequested(outputSelection, file, name, "metadata"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "metadata"))
contractData["metadata"] = m_compilerStack.metadata(contractName);
if (isArtifactRequested(outputSelection, file, name, "userdoc"))
contractData["userdoc"] = m_compilerStack.natspecUser(contractName);
@@ -578,16 +733,16 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
// EVM
Json::Value evmData(Json::objectValue);
// @TODO: add ir
- if (isArtifactRequested(outputSelection, file, name, "evm.assembly"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.assembly"))
evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input));
- if (isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly"))
evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input));
if (isArtifactRequested(outputSelection, file, name, "evm.methodIdentifiers"))
evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName);
- if (isArtifactRequested(outputSelection, file, name, "evm.gasEstimates"))
+ if (compilationSuccess && isArtifactRequested(outputSelection, file, name, "evm.gasEstimates"))
evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName);
- if (isArtifactRequested(
+ if (compilationSuccess && isArtifactRequested(
outputSelection,
file,
name,
@@ -598,7 +753,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.sourceMapping(contractName)
);
- if (isArtifactRequested(
+ if (compilationSuccess && isArtifactRequested(
outputSelection,
file,
name,
@@ -609,14 +764,18 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.runtimeSourceMapping(contractName)
);
- contractData["evm"] = evmData;
-
- if (!contractsOutput.isMember(file))
- contractsOutput[file] = Json::objectValue;
+ if (!evmData.empty())
+ contractData["evm"] = evmData;
- contractsOutput[file][name] = contractData;
+ if (!contractData.empty())
+ {
+ if (!contractsOutput.isMember(file))
+ contractsOutput[file] = Json::objectValue;
+ contractsOutput[file][name] = contractData;
+ }
}
- output["contracts"] = contractsOutput;
+ if (!contractsOutput.empty())
+ output["contracts"] = contractsOutput;
return output;
}
diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp
index b785d557..efd46d40 100644
--- a/libsolidity/interface/Version.cpp
+++ b/libsolidity/interface/Version.cpp
@@ -21,11 +21,12 @@
*/
#include <libsolidity/interface/Version.h>
-#include <string>
+
+#include <liblangutil/Exceptions.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Common.h>
-#include <liblangutil/Exceptions.h>
#include <solidity/BuildInfo.h>
+#include <string>
using namespace dev;
using namespace dev::solidity;
diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h
index 24c3555d..38d63ec6 100644
--- a/libsolidity/interface/Version.h
+++ b/libsolidity/interface/Version.h
@@ -22,8 +22,8 @@
#pragma once
-#include <string>
#include <libdevcore/Common.h>
+#include <string>
namespace dev
{
diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp
index d8927fea..d1d45150 100644
--- a/libsolidity/parsing/DocStringParser.cpp
+++ b/libsolidity/parsing/DocStringParser.cpp
@@ -1,17 +1,33 @@
+/*
+ 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/parsing/DocStringParser.h>
+
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
-#include <boost/range/irange.hpp>
#include <boost/range/algorithm.hpp>
+#include <boost/range/irange.hpp>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
-
namespace
{
diff --git a/libsolidity/parsing/DocStringParser.h b/libsolidity/parsing/DocStringParser.h
index c83b416d..671a2f34 100644
--- a/libsolidity/parsing/DocStringParser.h
+++ b/libsolidity/parsing/DocStringParser.h
@@ -22,8 +22,8 @@
#pragma once
-#include <string>
#include <libsolidity/ast/ASTAnnotations.h>
+#include <string>
namespace langutil
{
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 6cab7be3..8a6bc343 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -20,13 +20,17 @@
* Solidity parser.
*/
-#include <cctype>
-#include <vector>
#include <libsolidity/parsing/Parser.h>
+
+#include <libsolidity/analysis/SemVerHandler.h>
+#include <libsolidity/interface/Version.h>
#include <libyul/AsmParser.h>
-#include <liblangutil/SourceLocation.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Scanner.h>
+#include <liblangutil/SourceLocation.h>
+#include <cctype>
+#include <vector>
using namespace std;
using namespace langutil;
@@ -42,9 +46,9 @@ class Parser::ASTNodeFactory
{
public:
explicit ASTNodeFactory(Parser const& _parser):
- m_parser(_parser), m_location(_parser.position(), -1, _parser.source()) {}
+ m_parser(_parser), m_location{_parser.position(), -1, _parser.source()} {}
ASTNodeFactory(Parser const& _parser, ASTPointer<ASTNode> const& _childNode):
- m_parser(_parser), m_location(_childNode->location()) {}
+ m_parser(_parser), m_location{_childNode->location()} {}
void markEndPosition() { m_location.end = m_parser.endPosition(); }
void setLocation(SourceLocation const& _location) { m_location = _location; }
@@ -104,6 +108,20 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
}
}
+void Parser::parsePragmaVersion(vector<Token> const& tokens, vector<string> const& literals)
+{
+ SemVerMatchExpressionParser parser(tokens, literals);
+ auto matchExpression = parser.parse();
+ static SemVerVersion const currentVersion{string(VersionString)};
+ // FIXME: only match for major version incompatibility
+ if (!matchExpression.matches(currentVersion))
+ fatalParserError(
+ "Source file requires different compiler version (current compiler is " +
+ string(VersionString) + " - note that nightly builds are considered to be "
+ "strictly less than the released version"
+ );
+}
+
ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
{
RecursionGuard recursionGuard(*this);
@@ -132,6 +150,15 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
while (m_scanner->currentToken() != Token::Semicolon && m_scanner->currentToken() != Token::EOS);
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
+
+ if (literals.size() >= 2 && literals[0] == "solidity")
+ {
+ parsePragmaVersion(
+ vector<Token>(tokens.begin() + 1, tokens.end()),
+ vector<string>(literals.begin() + 1, literals.end())
+ );
+ }
+
return nodeFactory.createNode<PragmaDirective>(tokens, literals);
}
@@ -170,7 +197,7 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
expectToken(Token::As);
alias = expectIdentifierToken();
}
- symbolAliases.push_back(make_pair(move(id), move(alias)));
+ symbolAliases.emplace_back(move(id), move(alias));
if (m_scanner->currentToken() != Token::Comma)
break;
m_scanner->next();
@@ -1012,7 +1039,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
m_scanner->next();
}
- yul::Parser asmParser(m_errorReporter);
+ yul::Parser asmParser(m_errorReporter, yul::EVMDialect::looseAssemblyForEVM());
shared_ptr<yul::Block> block = asmParser.parse(m_scanner, true);
nodeFactory.markEndPosition();
return nodeFactory.createNode<InlineAssembly>(_docString, block);
@@ -1690,7 +1717,7 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath()
index = parseExpression();
SourceLocation indexLocation = iap.path.front()->location();
indexLocation.end = endPosition();
- iap.indices.push_back(make_pair(index, indexLocation));
+ iap.indices.emplace_back(index, indexLocation);
expectToken(Token::RBrack);
}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 15852096..b8d0e9a8 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -47,7 +47,10 @@ private:
struct VarDeclParserOptions
{
+ // This is actually not needed, but due to a defect in the C++ standard, we have to.
+ // https://stackoverflow.com/questions/17430377
VarDeclParserOptions() {}
+
bool allowVar = false;
bool isStateVariable = false;
bool allowIndexed = false;
@@ -70,6 +73,7 @@ private:
///@{
///@name Parsing functions for the AST nodes
+ void parsePragmaVersion(std::vector<Token> const& tokens, std::vector<std::string> const& literals);
ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<ImportDirective> parseImportDirective();
ContractDefinition::ContractKind parseContractKind();
@@ -84,7 +88,7 @@ private:
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();
ASTPointer<VariableDeclaration> parseVariableDeclaration(
- VarDeclParserOptions const& _options = VarDeclParserOptions(),
+ VarDeclParserOptions const& _options = {},
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
);
ASTPointer<ModifierDefinition> parseModifierDefinition();
@@ -98,7 +102,7 @@ private:
ASTPointer<FunctionTypeName> parseFunctionType();
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(
- VarDeclParserOptions const& _options,
+ VarDeclParserOptions const& _options = {},
bool _allowEmpty = true
);
ASTPointer<Block> parseBlock(ASTPointer<ASTString> const& _docString = {});
diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp
index d3f6de84..0ecc5a30 100644
--- a/libyul/AsmAnalysis.cpp
+++ b/libyul/AsmAnalysis.cpp
@@ -101,8 +101,8 @@ bool AsmAnalyzer::operator()(Literal const& _literal)
}
else if (_literal.kind == LiteralKind::Boolean)
{
- solAssert(m_flavour == AsmFlavour::Yul, "");
- solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, "");
+ solAssert(m_dialect->flavour == AsmFlavour::Yul, "");
+ solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "");
}
m_info.stackHeightInfo[&_literal] = m_stackHeight;
return true;
@@ -164,7 +164,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier)
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
{
- solAssert(m_flavour != AsmFlavour::Yul, "");
+ solAssert(m_dialect->flavour != AsmFlavour::Yul, "");
bool success = true;
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
if (!expectExpression(arg))
@@ -182,9 +182,9 @@ bool AsmAnalyzer::operator()(ExpressionStatement const& _statement)
{
int initialStackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, _statement.expression);
- if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose))
+ if (m_stackHeight != initialStackHeight && (m_dialect->flavour != AsmFlavour::Loose || m_errorTypeForLoose))
{
- Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
+ Error::Type errorType = m_dialect->flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
string msg =
"Top-level expressions are not supposed to return values (this expression returns " +
to_string(m_stackHeight - initialStackHeight) +
@@ -244,9 +244,18 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
{
int const stackHeight = m_stackHeight;
success = boost::apply_visitor(*this, *_varDecl.value);
- if ((m_stackHeight - stackHeight) != numVariables)
+ int numValues = m_stackHeight - stackHeight;
+ if (numValues != numVariables)
{
- m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch.");
+ m_errorReporter.declarationError(_varDecl.location,
+ "Variable count mismatch: " +
+ to_string(numVariables) +
+ " variables and " +
+ to_string(numValues) +
+ " values."
+ );
+ // Adjust stack height to avoid misleading additional errors.
+ m_stackHeight += numVariables - numValues;
return false;
}
}
@@ -288,9 +297,15 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
{
solAssert(!_funCall.functionName.name.empty(), "");
bool success = true;
- size_t arguments = 0;
+ size_t parameters = 0;
size_t returns = 0;
- if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
+ if (BuiltinFunction const* f = m_dialect->builtin(_funCall.functionName.name))
+ {
+ // TODO: compare types, too
+ parameters = f->parameters.size();
+ returns = f->returns.size();
+ }
+ else if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor(
[&](Scope::Variable const&)
{
m_errorReporter.typeError(
@@ -310,7 +325,7 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
[&](Scope::Function const& _fun)
{
/// TODO: compare types too
- arguments = _fun.arguments.size();
+ parameters = _fun.arguments.size();
returns = _fun.returns.size();
}
)))
@@ -319,21 +334,23 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
success = false;
}
if (success)
- {
- if (_funCall.arguments.size() != arguments)
+ if (_funCall.arguments.size() != parameters)
{
m_errorReporter.typeError(
_funCall.functionName.location,
- "Expected " + to_string(arguments) + " arguments but got " +
+ "Function expects " +
+ to_string(parameters) +
+ " arguments but got " +
to_string(_funCall.arguments.size()) + "."
);
success = false;
}
- }
+
for (auto const& arg: _funCall.arguments | boost::adaptors::reversed)
if (!expectExpression(arg))
success = false;
- m_stackHeight += int(returns) - int(arguments);
+ // Use argument size instead of parameter count to avoid misleading errors.
+ m_stackHeight += int(returns) - int(_funCall.arguments.size());
m_info.stackHeightInfo[&_funCall] = m_stackHeight;
return success;
}
@@ -552,7 +569,7 @@ Scope& AsmAnalyzer::scope(Block const* _block)
}
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
{
- if (m_flavour != AsmFlavour::Yul)
+ if (m_dialect->flavour != AsmFlavour::Yul)
return;
if (!builtinTypes.count(type))
@@ -612,7 +629,9 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
{
- solAssert(m_flavour == AsmFlavour::Loose, "");
+ if (m_dialect->flavour != AsmFlavour::Loose)
+ solAssert(m_errorTypeForLoose && *m_errorTypeForLoose != Error::Type::Warning, "");
+
m_errorReporter.error(
m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning,
_location,
@@ -625,7 +644,7 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
{
- if (m_flavour != AsmFlavour::Loose)
+ if (m_dialect->flavour != AsmFlavour::Loose)
solAssert(false, _description);
else if (m_errorTypeForLoose)
m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h
index 34e32eb0..21cc1142 100644
--- a/libyul/AsmAnalysis.h
+++ b/libyul/AsmAnalysis.h
@@ -23,12 +23,12 @@
#include <liblangutil/Exceptions.h>
#include <liblangutil/EVMVersion.h>
+#include <libyul/Dialect.h>
#include <libyul/AsmScope.h>
+#include <libyul/AsmDataForward.h>
#include <libyul/backends/evm/AbstractAssembly.h>
-#include <libyul/AsmDataForward.h>
-
#include <boost/variant.hpp>
#include <boost/optional.hpp>
@@ -59,14 +59,14 @@ public:
langutil::ErrorReporter& _errorReporter,
dev::solidity::EVMVersion _evmVersion,
boost::optional<langutil::Error::Type> _errorTypeForLoose,
- AsmFlavour _flavour = AsmFlavour::Loose,
+ std::shared_ptr<Dialect> _dialect,
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver()
):
m_resolver(_resolver),
m_info(_analysisInfo),
m_errorReporter(_errorReporter),
m_evmVersion(_evmVersion),
- m_flavour(_flavour),
+ m_dialect(std::move(_dialect)),
m_errorTypeForLoose(_errorTypeForLoose)
{}
@@ -115,7 +115,7 @@ private:
AsmAnalysisInfo& m_info;
langutil::ErrorReporter& m_errorReporter;
dev::solidity::EVMVersion m_evmVersion;
- AsmFlavour m_flavour = AsmFlavour::Loose;
+ std::shared_ptr<Dialect> m_dialect;
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
};
diff --git a/libyul/AsmCodeGen.cpp b/libyul/AsmCodeGen.cpp
deleted file mode 100644
index 23bf395d..00000000
--- a/libyul/AsmCodeGen.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- 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
- * Code-generating part of inline assembly.
- */
-
-#include <libyul/AsmCodeGen.h>
-
-#include <libyul/AsmParser.h>
-#include <libyul/AsmData.h>
-#include <libyul/AsmScope.h>
-#include <libyul/AsmAnalysis.h>
-#include <libyul/AsmAnalysisInfo.h>
-
-#include <libyul/backends/evm/AbstractAssembly.h>
-#include <libyul/backends/evm/EVMCodeTransform.h>
-
-#include <libevmasm/Assembly.h>
-#include <libevmasm/Instruction.h>
-
-#include <liblangutil/SourceLocation.h>
-
-#include <libdevcore/CommonIO.h>
-
-#include <boost/range/adaptor/reversed.hpp>
-#include <boost/range/adaptor/map.hpp>
-#include <boost/range/algorithm/count_if.hpp>
-
-#include <memory>
-#include <functional>
-
-using namespace std;
-using namespace dev;
-using namespace langutil;
-using namespace yul;
-using namespace dev::solidity;
-
-class EthAssemblyAdapter: public AbstractAssembly
-{
-public:
- explicit EthAssemblyAdapter(eth::Assembly& _assembly):
- m_assembly(_assembly)
- {
- }
- virtual void setSourceLocation(SourceLocation const& _location) override
- {
- m_assembly.setSourceLocation(_location);
- }
- virtual int stackHeight() const override { return m_assembly.deposit(); }
- virtual void appendInstruction(solidity::Instruction _instruction) override
- {
- m_assembly.append(_instruction);
- }
- virtual void appendConstant(u256 const& _constant) override
- {
- m_assembly.append(_constant);
- }
- /// Append a label.
- virtual void appendLabel(LabelID _labelId) override
- {
- m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
- }
- /// Append a label reference.
- virtual void appendLabelReference(LabelID _labelId) override
- {
- m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
- }
- virtual size_t newLabelId() override
- {
- return assemblyTagToIdentifier(m_assembly.newTag());
- }
- virtual size_t namedLabel(std::string const& _name) override
- {
- return assemblyTagToIdentifier(m_assembly.namedTag(_name));
- }
- virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override
- {
- m_assembly.appendLibraryAddress(_linkerSymbol);
- }
- virtual void appendJump(int _stackDiffAfter) override
- {
- appendInstruction(solidity::Instruction::JUMP);
- m_assembly.adjustDeposit(_stackDiffAfter);
- }
- virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override
- {
- appendLabelReference(_labelId);
- appendJump(_stackDiffAfter);
- }
- virtual void appendJumpToIf(LabelID _labelId) override
- {
- appendLabelReference(_labelId);
- appendInstruction(solidity::Instruction::JUMPI);
- }
- virtual void appendBeginsub(LabelID, int) override
- {
- // TODO we could emulate that, though
- solAssert(false, "BEGINSUB not implemented for EVM 1.0");
- }
- /// Call a subroutine.
- virtual void appendJumpsub(LabelID, int, int) override
- {
- // TODO we could emulate that, though
- solAssert(false, "JUMPSUB not implemented for EVM 1.0");
- }
-
- /// Return from a subroutine.
- virtual void appendReturnsub(int, int) override
- {
- // TODO we could emulate that, though
- solAssert(false, "RETURNSUB not implemented for EVM 1.0");
- }
-
- virtual void appendAssemblySize() override
- {
- m_assembly.appendProgramSize();
- }
-
-private:
- static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
- {
- u256 id = _tag.data();
- solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
- return LabelID(id);
- }
-
- eth::Assembly& m_assembly;
-};
-
-void CodeGenerator::assemble(
- Block const& _parsedData,
- AsmAnalysisInfo& _analysisInfo,
- eth::Assembly& _assembly,
- ExternalIdentifierAccess const& _identifierAccess,
- bool _useNamedLabelsForFunctions
-)
-{
- EthAssemblyAdapter assemblyAdapter(_assembly);
- CodeTransform(
- assemblyAdapter,
- _analysisInfo,
- false,
- false,
- _identifierAccess,
- _useNamedLabelsForFunctions
- )(_parsedData);
-}
diff --git a/libyul/AsmDataForward.h b/libyul/AsmDataForward.h
index 046c8248..de564425 100644
--- a/libyul/AsmDataForward.h
+++ b/libyul/AsmDataForward.h
@@ -49,11 +49,4 @@ struct TypedName;
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
-enum class AsmFlavour
-{
- Loose, // no types, EVM instructions as function, jumps and direct stack manipulations
- Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations
- Yul // same as Strict mode with types
-};
-
}
diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp
index 2ce94f85..f3ca6cd0 100644
--- a/libyul/AsmParser.cpp
+++ b/libyul/AsmParser.cpp
@@ -107,14 +107,16 @@ Statement Parser::parseStatement()
return parseForLoop();
case Token::Assign:
{
- if (m_flavour != AsmFlavour::Loose)
+ if (m_dialect->flavour != AsmFlavour::Loose)
break;
StackAssignment assignment = createWithLocation<StackAssignment>();
advance();
expectToken(Token::Colon);
assignment.variableName.location = location();
assignment.variableName.name = YulString(currentLiteral());
- if (instructions().count(assignment.variableName.name.str()))
+ if (m_dialect->builtin(assignment.variableName.name))
+ fatalParserError("Identifier expected, got builtin symbol.");
+ else if (instructions().count(assignment.variableName.name.str()))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
@@ -174,7 +176,9 @@ Statement Parser::parseStatement()
if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
{
Assignment assignment = createWithLocation<Assignment>(identifier.location);
- if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
+ if (m_dialect->builtin(identifier.name))
+ fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
+ else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(identifier.name.str()))
fatalParserError("Cannot use instruction names for identifier names.");
advance();
assignment.variableNames.emplace_back(identifier);
@@ -185,7 +189,7 @@ Statement Parser::parseStatement()
else
{
// label
- if (m_flavour != AsmFlavour::Loose)
+ if (m_dialect->flavour != AsmFlavour::Loose)
fatalParserError("Labels are not supported.");
Label label = createWithLocation<Label>(identifier.location);
label.name = identifier.name;
@@ -193,7 +197,7 @@ Statement Parser::parseStatement()
}
}
default:
- if (m_flavour != AsmFlavour::Loose)
+ if (m_dialect->flavour != AsmFlavour::Loose)
fatalParserError("Call or assignment expected.");
break;
}
@@ -269,7 +273,7 @@ Expression Parser::parseExpression()
instructionNames().at(instr.instruction) +
"\" not allowed in this context."
);
- if (m_flavour != AsmFlavour::Loose && currentToken() != Token::LParen)
+ if (m_dialect->flavour != AsmFlavour::Loose && currentToken() != Token::LParen)
fatalParserError(
"Non-functional instructions are not allowed in this context."
);
@@ -289,7 +293,7 @@ Expression Parser::parseExpression()
else if (operation.type() == typeid(Instruction))
{
// Instructions not taking arguments are allowed as expressions.
- solAssert(m_flavour == AsmFlavour::Loose, "");
+ solAssert(m_dialect->flavour == AsmFlavour::Loose, "");
Instruction& instr = boost::get<Instruction>(operation);
return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
}
@@ -348,23 +352,25 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
case Token::Byte:
case Token::Address:
{
- string literal;
+ YulString literal;
if (currentToken() == Token::Return)
- literal = "return";
+ literal = "return"_yulstring;
else if (currentToken() == Token::Byte)
- literal = "byte";
+ literal = "byte"_yulstring;
else if (currentToken() == Token::Address)
- literal = "address";
+ literal = "address"_yulstring;
else
- literal = currentLiteral();
- // first search the set of instructions.
- if (m_flavour != AsmFlavour::Yul && instructions().count(literal))
+ literal = YulString{currentLiteral()};
+ // first search the set of builtins, then the instructions.
+ if (m_dialect->builtin(literal))
+ ret = Identifier{location(), literal};
+ else if (m_dialect->flavour != AsmFlavour::Yul && instructions().count(literal.str()))
{
- dev::solidity::Instruction const& instr = instructions().at(literal);
+ dev::solidity::Instruction const& instr = instructions().at(literal.str());
ret = Instruction{location(), instr};
}
else
- ret = Identifier{location(), YulString{literal}};
+ ret = Identifier{location(), literal};
advance();
break;
}
@@ -399,11 +405,11 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
{}
};
advance();
- if (m_flavour == AsmFlavour::Yul)
+ if (m_dialect->flavour == AsmFlavour::Yul)
{
expectToken(Token::Colon);
literal.location.end = endPosition();
- literal.type = YulString{expectAsmIdentifier()};
+ literal.type = expectAsmIdentifier();
}
else if (kind == LiteralKind::Boolean)
fatalParserError("True and false are not valid literals.");
@@ -412,7 +418,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
}
default:
fatalParserError(
- m_flavour == AsmFlavour::Yul ?
+ m_dialect->flavour == AsmFlavour::Yul ?
"Literal or identifier expected." :
"Literal, identifier or instruction expected."
);
@@ -450,7 +456,7 @@ FunctionDefinition Parser::parseFunctionDefinition()
RecursionGuard recursionGuard(*this);
FunctionDefinition funDef = createWithLocation<FunctionDefinition>();
expectToken(Token::Function);
- funDef.name = YulString{expectAsmIdentifier()};
+ funDef.name = expectAsmIdentifier();
expectToken(Token::LParen);
while (currentToken() != Token::RParen)
{
@@ -482,7 +488,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
RecursionGuard recursionGuard(*this);
if (_initialOp.type() == typeid(Instruction))
{
- solAssert(m_flavour != AsmFlavour::Yul, "Instructions are invalid in Yul");
+ solAssert(m_dialect->flavour != AsmFlavour::Yul, "Instructions are invalid in Yul");
Instruction& instruction = boost::get<Instruction>(_initialOp);
FunctionalInstruction ret;
ret.instruction = instruction.instruction;
@@ -553,7 +559,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
}
else
fatalParserError(
- m_flavour == AsmFlavour::Yul ?
+ m_dialect->flavour == AsmFlavour::Yul ?
"Function name expected." :
"Assembly instruction or function name required in front of \"(\")"
);
@@ -565,20 +571,20 @@ TypedName Parser::parseTypedName()
{
RecursionGuard recursionGuard(*this);
TypedName typedName = createWithLocation<TypedName>();
- typedName.name = YulString{expectAsmIdentifier()};
- if (m_flavour == AsmFlavour::Yul)
+ typedName.name = expectAsmIdentifier();
+ if (m_dialect->flavour == AsmFlavour::Yul)
{
expectToken(Token::Colon);
typedName.location.end = endPosition();
- typedName.type = YulString{expectAsmIdentifier()};
+ typedName.type = expectAsmIdentifier();
}
return typedName;
}
-string Parser::expectAsmIdentifier()
+YulString Parser::expectAsmIdentifier()
{
- string name = currentLiteral();
- if (m_flavour == AsmFlavour::Yul)
+ YulString name = YulString{currentLiteral()};
+ if (m_dialect->flavour == AsmFlavour::Yul)
{
switch (currentToken())
{
@@ -592,7 +598,9 @@ string Parser::expectAsmIdentifier()
break;
}
}
- else if (instructions().count(name))
+ else if (m_dialect->builtin(name))
+ fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
+ else if (instructions().count(name.str()))
fatalParserError("Cannot use instruction names for identifier names.");
expectToken(Token::Identifier);
return name;
diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h
index 52166a20..a02f0b4d 100644
--- a/libyul/AsmParser.h
+++ b/libyul/AsmParser.h
@@ -22,21 +22,24 @@
#pragma once
-#include <memory>
-#include <vector>
#include <libyul/AsmData.h>
+#include <libyul/Dialect.h>
+
#include <liblangutil/SourceLocation.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ParserBase.h>
+#include <memory>
+#include <vector>
+
namespace yul
{
class Parser: public langutil::ParserBase
{
public:
- explicit Parser(langutil::ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose):
- ParserBase(_errorReporter), m_flavour(_flavour) {}
+ explicit Parser(langutil::ErrorReporter& _errorReporter, std::shared_ptr<Dialect> _dialect):
+ ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {}
/// Parses an inline assembly block starting with `{` and ending with `}`.
/// @param _reuseScanner if true, do check for end of input after the `}`.
@@ -78,12 +81,12 @@ protected:
FunctionDefinition parseFunctionDefinition();
Expression parseCall(ElementaryOperation&& _initialOp);
TypedName parseTypedName();
- std::string expectAsmIdentifier();
+ YulString expectAsmIdentifier();
static bool isValidNumberLiteral(std::string const& _literal);
private:
- AsmFlavour m_flavour = AsmFlavour::Loose;
+ std::shared_ptr<Dialect> m_dialect;
};
}
diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp
index eaaba9f3..b7af4778 100644
--- a/libyul/AsmPrinter.cpp
+++ b/libyul/AsmPrinter.cpp
@@ -40,14 +40,14 @@ using namespace dev::solidity;
//@TODO source locations
-string AsmPrinter::operator()(yul::Instruction const& _instruction)
+string AsmPrinter::operator()(yul::Instruction const& _instruction) const
{
solAssert(!m_yul, "");
solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction");
return boost::to_lower_copy(instructionInfo(_instruction.instruction).name);
}
-string AsmPrinter::operator()(Literal const& _literal)
+string AsmPrinter::operator()(Literal const& _literal) const
{
switch (_literal.kind)
{
@@ -55,8 +55,8 @@ string AsmPrinter::operator()(Literal const& _literal)
solAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal");
return _literal.value.str() + appendTypeName(_literal.type);
case LiteralKind::Boolean:
- solAssert(_literal.value.str() == "true" || _literal.value.str() == "false", "Invalid bool literal.");
- return ((_literal.value.str() == "true") ? "true" : "false") + appendTypeName(_literal.type);
+ solAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, "Invalid bool literal.");
+ return ((_literal.value == "true"_yulstring) ? "true" : "false") + appendTypeName(_literal.type);
case LiteralKind::String:
break;
}
@@ -90,13 +90,13 @@ string AsmPrinter::operator()(Literal const& _literal)
return "\"" + out + "\"" + appendTypeName(_literal.type);
}
-string AsmPrinter::operator()(Identifier const& _identifier)
+string AsmPrinter::operator()(Identifier const& _identifier) const
{
solAssert(!_identifier.name.empty(), "Invalid identifier.");
return _identifier.name.str();
}
-string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstruction)
+string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstruction) const
{
solAssert(!m_yul, "");
solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction");
@@ -109,26 +109,26 @@ string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstructio
")";
}
-string AsmPrinter::operator()(ExpressionStatement const& _statement)
+string AsmPrinter::operator()(ExpressionStatement const& _statement) const
{
return boost::apply_visitor(*this, _statement.expression);
}
-string AsmPrinter::operator()(Label const& _label)
+string AsmPrinter::operator()(Label const& _label) const
{
solAssert(!m_yul, "");
solAssert(!_label.name.empty(), "Invalid label.");
return _label.name.str() + ":";
}
-string AsmPrinter::operator()(StackAssignment const& _assignment)
+string AsmPrinter::operator()(StackAssignment const& _assignment) const
{
solAssert(!m_yul, "");
solAssert(!_assignment.variableName.name.empty(), "Invalid variable name.");
return "=: " + (*this)(_assignment.variableName);
}
-string AsmPrinter::operator()(Assignment const& _assignment)
+string AsmPrinter::operator()(Assignment const& _assignment) const
{
solAssert(_assignment.variableNames.size() >= 1, "");
string variables = (*this)(_assignment.variableNames.front());
@@ -137,7 +137,7 @@ string AsmPrinter::operator()(Assignment const& _assignment)
return variables + " := " + boost::apply_visitor(*this, *_assignment.value);
}
-string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration)
+string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) const
{
string out = "let ";
out += boost::algorithm::join(
@@ -154,7 +154,7 @@ string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration)
return out;
}
-string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition)
+string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) const
{
solAssert(!_functionDefinition.name.empty(), "Invalid function name.");
string out = "function " + _functionDefinition.name.str() + "(";
@@ -179,7 +179,7 @@ string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition)
return out + "\n" + (*this)(_functionDefinition.body);
}
-string AsmPrinter::operator()(FunctionCall const& _functionCall)
+string AsmPrinter::operator()(FunctionCall const& _functionCall) const
{
return
(*this)(_functionCall.functionName) + "(" +
@@ -189,13 +189,13 @@ string AsmPrinter::operator()(FunctionCall const& _functionCall)
")";
}
-string AsmPrinter::operator()(If const& _if)
+string AsmPrinter::operator()(If const& _if) const
{
solAssert(_if.condition, "Invalid if condition.");
return "if " + boost::apply_visitor(*this, *_if.condition) + "\n" + (*this)(_if.body);
}
-string AsmPrinter::operator()(Switch const& _switch)
+string AsmPrinter::operator()(Switch const& _switch) const
{
solAssert(_switch.expression, "Invalid expression pointer.");
string out = "switch " + boost::apply_visitor(*this, *_switch.expression);
@@ -210,7 +210,7 @@ string AsmPrinter::operator()(Switch const& _switch)
return out;
}
-string AsmPrinter::operator()(ForLoop const& _forLoop)
+string AsmPrinter::operator()(ForLoop const& _forLoop) const
{
solAssert(_forLoop.condition, "Invalid for loop condition.");
string out = "for ";
@@ -224,7 +224,7 @@ string AsmPrinter::operator()(ForLoop const& _forLoop)
return out;
}
-string AsmPrinter::operator()(Block const& _block)
+string AsmPrinter::operator()(Block const& _block) const
{
if (_block.statements.empty())
return "{\n}";
diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h
index 61dfc18c..a1b9d6cd 100644
--- a/libyul/AsmPrinter.h
+++ b/libyul/AsmPrinter.h
@@ -36,21 +36,21 @@ class AsmPrinter: public boost::static_visitor<std::string>
public:
explicit AsmPrinter(bool _yul = false): m_yul(_yul) {}
- std::string operator()(Instruction const& _instruction);
- std::string operator()(Literal const& _literal);
- std::string operator()(Identifier const& _identifier);
- std::string operator()(FunctionalInstruction const& _functionalInstruction);
- std::string operator()(ExpressionStatement const& _expr);
- std::string operator()(Label const& _label);
- std::string operator()(StackAssignment const& _assignment);
- std::string operator()(Assignment const& _assignment);
- std::string operator()(VariableDeclaration const& _variableDeclaration);
- std::string operator()(FunctionDefinition const& _functionDefinition);
- std::string operator()(FunctionCall const& _functionCall);
- std::string operator()(If const& _if);
- std::string operator()(Switch const& _switch);
- std::string operator()(ForLoop const& _forLoop);
- std::string operator()(Block const& _block);
+ std::string operator()(Instruction const& _instruction) const;
+ std::string operator()(Literal const& _literal) const;
+ std::string operator()(Identifier const& _identifier) const;
+ std::string operator()(FunctionalInstruction const& _functionalInstruction) const;
+ std::string operator()(ExpressionStatement const& _expr) const;
+ std::string operator()(Label const& _label) const;
+ std::string operator()(StackAssignment const& _assignment) const;
+ std::string operator()(Assignment const& _assignment) const;
+ std::string operator()(VariableDeclaration const& _variableDeclaration) const;
+ std::string operator()(FunctionDefinition const& _functionDefinition) const;
+ std::string operator()(FunctionCall const& _functionCall) const;
+ std::string operator()(If const& _if) const;
+ std::string operator()(Switch const& _switch) const;
+ std::string operator()(ForLoop const& _forLoop) const;
+ std::string operator()(Block const& _block) const;
private:
std::string formatTypedName(TypedName _variable) const;
diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt
index 7ed84ff5..52c4ac8e 100644
--- a/libyul/CMakeLists.txt
+++ b/libyul/CMakeLists.txt
@@ -1,45 +1,98 @@
add_library(yul
AsmAnalysis.cpp
+ AsmAnalysis.h
AsmAnalysisInfo.cpp
- AsmCodeGen.cpp
+ AsmAnalysisInfo.h
+ AsmData.h
+ AsmDataForward.h
AsmParser.cpp
+ AsmParser.h
AsmPrinter.cpp
+ AsmPrinter.h
AsmScope.cpp
+ AsmScope.h
AsmScopeFiller.cpp
+ AsmScopeFiller.h
+ Dialect.cpp
+ Dialect.h
+ Exceptions.h
Object.cpp
+ Object.h
ObjectParser.cpp
+ ObjectParser.h
+ YulString.h
+ backends/evm/AbstractAssembly.h
backends/evm/EVMAssembly.cpp
+ backends/evm/EVMAssembly.h
backends/evm/EVMCodeTransform.cpp
+ backends/evm/EVMCodeTransform.h
+ backends/evm/EVMDialect.cpp
+ backends/evm/EVMDialect.h
+ backends/evm/EVMObjectCompiler.cpp
+ backends/evm/EVMObjectCompiler.h
optimiser/ASTCopier.cpp
+ optimiser/ASTCopier.h
optimiser/ASTWalker.cpp
+ optimiser/ASTWalker.h
optimiser/BlockFlattener.cpp
+ optimiser/BlockFlattener.h
optimiser/CommonSubexpressionEliminator.cpp
+ optimiser/CommonSubexpressionEliminator.h
optimiser/DataFlowAnalyzer.cpp
+ optimiser/DataFlowAnalyzer.h
optimiser/Disambiguator.cpp
+ optimiser/Disambiguator.h
optimiser/ExpressionInliner.cpp
+ optimiser/ExpressionInliner.h
optimiser/ExpressionJoiner.cpp
+ optimiser/ExpressionJoiner.h
optimiser/ExpressionSimplifier.cpp
+ optimiser/ExpressionSimplifier.h
optimiser/ExpressionSplitter.cpp
+ optimiser/ExpressionSplitter.h
optimiser/ForLoopInitRewriter.cpp
+ optimiser/ForLoopInitRewriter.h
optimiser/FullInliner.cpp
+ optimiser/FullInliner.h
optimiser/FunctionGrouper.cpp
+ optimiser/FunctionGrouper.h
optimiser/FunctionHoister.cpp
+ optimiser/FunctionHoister.h
optimiser/InlinableExpressionFunctionFinder.cpp
+ optimiser/InlinableExpressionFunctionFinder.h
optimiser/MainFunction.cpp
+ optimiser/MainFunction.h
optimiser/Metrics.cpp
+ optimiser/Metrics.h
optimiser/NameCollector.cpp
+ optimiser/NameCollector.h
optimiser/NameDispenser.cpp
+ optimiser/NameDispenser.h
optimiser/RedundantAssignEliminator.cpp
+ optimiser/RedundantAssignEliminator.h
optimiser/Rematerialiser.cpp
+ optimiser/Rematerialiser.h
optimiser/SSATransform.cpp
+ optimiser/SSATransform.h
optimiser/SSAValueTracker.cpp
+ optimiser/SSAValueTracker.h
optimiser/Semantics.cpp
+ optimiser/Semantics.h
optimiser/SimplificationRules.cpp
+ optimiser/SimplificationRules.h
+ optimiser/StructuralSimplifier.cpp
+ optimiser/StructuralSimplifier.h
optimiser/Substitution.cpp
+ optimiser/Substitution.h
optimiser/Suite.cpp
+ optimiser/Suite.h
optimiser/SyntacticalEquality.cpp
+ optimiser/SyntacticalEquality.h
optimiser/UnusedPruner.cpp
+ optimiser/UnusedPruner.h
optimiser/Utilities.cpp
- optimiser/VarDeclPropagator.cpp
+ optimiser/Utilities.h
+ optimiser/VarDeclInitializer.cpp
+ optimiser/VarDeclInitializer.h
)
-target_link_libraries(yul PUBLIC devcore)
+target_link_libraries(yul PUBLIC evmasm devcore langutil)
diff --git a/libyul/Dialect.cpp b/libyul/Dialect.cpp
new file mode 100644
index 00000000..f6985c17
--- /dev/null
+++ b/libyul/Dialect.cpp
@@ -0,0 +1,29 @@
+/*
+ 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/>.
+*/
+/**
+ * Yul dialect.
+ */
+
+#include <libyul/Dialect.h>
+
+#include <libyul/Object.h>
+#include <libyul/backends/evm/AbstractAssembly.h>
+
+#include <map>
+
+using namespace yul;
+using namespace std;
diff --git a/libyul/Dialect.h b/libyul/Dialect.h
new file mode 100644
index 00000000..01fd98df
--- /dev/null
+++ b/libyul/Dialect.h
@@ -0,0 +1,66 @@
+/*
+ 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/>.
+*/
+/**
+ * Yul dialect.
+ */
+
+#pragma once
+
+#include <libyul/YulString.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <vector>
+
+namespace yul
+{
+
+class YulString;
+using Type = YulString;
+
+enum class AsmFlavour
+{
+ Loose, // no types, EVM instructions as function, jumps and direct stack manipulations
+ Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations
+ Yul // same as Strict mode with types
+};
+
+struct BuiltinFunction
+{
+ YulString name;
+ std::vector<Type> parameters;
+ std::vector<Type> returns;
+ bool movable;
+};
+
+struct Dialect: boost::noncopyable
+{
+ AsmFlavour const flavour = AsmFlavour::Loose;
+ /// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
+ virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }
+
+ Dialect(AsmFlavour _flavour): flavour(_flavour) {}
+ virtual ~Dialect() = default;
+
+ static std::shared_ptr<Dialect> yul()
+ {
+ // Will have to add builtins later.
+ return std::make_shared<Dialect>(AsmFlavour::Yul);
+ }
+};
+
+}
diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h
index e10e53ef..d1f1fe96 100644
--- a/libyul/Exceptions.h
+++ b/libyul/Exceptions.h
@@ -28,6 +28,7 @@ namespace yul
struct YulException: virtual dev::Exception {};
struct OptimizerException: virtual YulException {};
+struct CodegenException: virtual YulException {};
struct YulAssertion: virtual YulException {};
/// Assertion that throws an YulAssertion containing the given description if it is not met.
diff --git a/libyul/Object.h b/libyul/Object.h
index cfd8d02d..8484eb53 100644
--- a/libyul/Object.h
+++ b/libyul/Object.h
@@ -37,7 +37,7 @@ struct AsmAnalysisInfo;
*/
struct ObjectNode
{
- virtual ~ObjectNode() {}
+ virtual ~ObjectNode() = default;
virtual std::string toString(bool _yul) const = 0;
YulString name;
diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp
index 43dd4be9..5f1eadef 100644
--- a/libyul/ObjectParser.cpp
+++ b/libyul/ObjectParser.cpp
@@ -42,7 +42,7 @@ shared_ptr<Object> ObjectParser::parse(shared_ptr<Scanner> const& _scanner, bool
{
// Special case: Code-only form.
object = make_shared<Object>();
- object->name = YulString{"object"};
+ object->name = "object"_yulstring;
object->code = parseBlock();
if (!object->code)
return nullptr;
@@ -104,7 +104,7 @@ shared_ptr<Block> ObjectParser::parseCode()
shared_ptr<Block> ObjectParser::parseBlock()
{
- Parser parser(m_errorReporter, m_flavour);
+ Parser parser(m_errorReporter, m_dialect);
shared_ptr<Block> block = parser.parse(m_scanner, true);
yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!");
return block;
diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h
index 1d88a119..e39f24cd 100644
--- a/libyul/ObjectParser.h
+++ b/libyul/ObjectParser.h
@@ -22,6 +22,7 @@
#include <libyul/YulString.h>
#include <libyul/Object.h>
+#include <libyul/Dialect.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/ParserBase.h>
@@ -46,9 +47,9 @@ class ObjectParser: public langutil::ParserBase
public:
explicit ObjectParser(
langutil::ErrorReporter& _errorReporter,
- yul::AsmFlavour _flavour = yul::AsmFlavour::Loose
+ std::shared_ptr<Dialect> _dialect
):
- ParserBase(_errorReporter), m_flavour(_flavour) {}
+ ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {}
/// Parses a Yul object.
/// Falls back to code-only parsing if the source starts with `{`.
@@ -66,7 +67,7 @@ private:
YulString parseUniqueName(Object const* _containingObject);
void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject);
- yul::AsmFlavour m_flavour;
+ std::shared_ptr<Dialect> m_dialect;
};
}
diff --git a/libyul/YulString.h b/libyul/YulString.h
index 2179c23b..5cea5619 100644
--- a/libyul/YulString.h
+++ b/libyul/YulString.h
@@ -42,10 +42,9 @@ public:
size_t id;
std::uint64_t hash;
};
- YulStringRepository():
- m_strings{std::make_shared<std::string>()},
- m_hashToID{std::make_pair(emptyHash(), 0)}
- {}
+
+ YulStringRepository() = default;
+
static YulStringRepository& instance()
{
static YulStringRepository inst;
@@ -80,9 +79,10 @@ public:
return hash;
}
static constexpr std::uint64_t emptyHash() { return 14695981039346656037u; }
+
private:
- std::vector<std::shared_ptr<std::string>> m_strings;
- std::unordered_multimap<std::uint64_t, size_t> m_hashToID;
+ std::vector<std::shared_ptr<std::string>> m_strings = {std::make_shared<std::string>()};
+ std::unordered_multimap<std::uint64_t, size_t> m_hashToID = {{emptyHash(), 0}};
};
/// Wrapper around handles into the YulString repository.
@@ -127,4 +127,9 @@ private:
YulStringRepository::Handle m_handle{ 0, YulStringRepository::emptyHash() };
};
+inline YulString operator "" _yulstring(const char *_string, std::size_t _size)
+{
+ return YulString(std::string(_string, _size));
+}
+
}
diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h
index 97b1d305..0cc41056 100644
--- a/libyul/backends/evm/AbstractAssembly.h
+++ b/libyul/backends/evm/AbstractAssembly.h
@@ -26,6 +26,7 @@
#include <libdevcore/CommonData.h>
#include <functional>
+#include <memory>
namespace langutil
{
@@ -52,8 +53,9 @@ class AbstractAssembly
{
public:
using LabelID = size_t;
+ using SubID = size_t;
- virtual ~AbstractAssembly() {}
+ virtual ~AbstractAssembly() = default;
/// Set a new source location valid starting from the next instruction.
virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0;
@@ -98,6 +100,14 @@ public:
/// Append the assembled size as a constant.
virtual void appendAssemblySize() = 0;
+ /// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
+ virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() = 0;
+ /// Appends the offset of the given sub-assembly or data.
+ virtual void appendDataOffset(SubID _sub) = 0;
+ /// Appends the size of the given sub-assembly or data.
+ virtual void appendDataSize(SubID _sub) = 0;
+ /// Appends the given data to the assembly and returns its ID.
+ virtual SubID appendData(dev::bytes const& _data) = 0;
};
enum class IdentifierContext { LValue, RValue };
diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp
index 99506317..2cf9f001 100644
--- a/libyul/backends/evm/EVMAssembly.cpp
+++ b/libyul/backends/evm/EVMAssembly.cpp
@@ -194,6 +194,27 @@ void EVMAssembly::appendAssemblySize()
m_bytecode += bytes(assemblySizeReferenceSize);
}
+pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EVMAssembly::createSubAssembly()
+{
+ solAssert(false, "Sub assemblies not implemented.");
+ return {};
+}
+
+void EVMAssembly::appendDataOffset(AbstractAssembly::SubID)
+{
+ solAssert(false, "Data not implemented.");
+}
+
+void EVMAssembly::appendDataSize(AbstractAssembly::SubID)
+{
+ solAssert(false, "Data not implemented.");
+}
+
+AbstractAssembly::SubID EVMAssembly::appendData(bytes const&)
+{
+ solAssert(false, "Data not implemented.");
+}
+
void EVMAssembly::updateReference(size_t pos, size_t size, u256 value)
{
solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, "");
diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h
index d0a437cc..e62bc87e 100644
--- a/libyul/backends/evm/EVMAssembly.h
+++ b/libyul/backends/evm/EVMAssembly.h
@@ -38,7 +38,7 @@ class EVMAssembly: public AbstractAssembly
{
public:
explicit EVMAssembly(bool _evm15 = false): m_evm15(_evm15) { }
- virtual ~EVMAssembly() {}
+ virtual ~EVMAssembly() = default;
/// Set a new source location valid starting from the next instruction.
void setSourceLocation(langutil::SourceLocation const& _location) override;
@@ -77,6 +77,10 @@ public:
/// Append the assembled size as a constant.
void appendAssemblySize() override;
+ std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
+ void appendDataOffset(SubID _sub) override;
+ void appendDataSize(SubID _sub) override;
+ SubID appendData(dev::bytes const& _data) override;
/// Resolves references inside the bytecode and returns the linker object.
dev::eth::LinkerObject finalize();
diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp
index 12abd754..04dc5040 100644
--- a/libyul/backends/evm/EVMCodeTransform.cpp
+++ b/libyul/backends/evm/EVMCodeTransform.cpp
@@ -20,6 +20,7 @@
#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/optimiser/NameCollector.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
@@ -32,6 +33,144 @@ using namespace dev;
using namespace yul;
using namespace dev::solidity;
+void VariableReferenceCounter::operator()(Identifier const& _identifier)
+{
+ increaseRefIfFound(_identifier.name);
+}
+
+void VariableReferenceCounter::operator()(FunctionDefinition const& _function)
+{
+ Scope* originalScope = m_scope;
+
+ solAssert(m_info.virtualBlocks.at(&_function), "");
+ m_scope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
+ solAssert(m_scope, "Variable scope does not exist.");
+
+ for (auto const& v: _function.returnVariables)
+ increaseRefIfFound(v.name);
+
+ VariableReferenceCounter{m_context, m_info}(_function.body);
+
+ m_scope = originalScope;
+}
+
+void VariableReferenceCounter::operator()(ForLoop const& _forLoop)
+{
+ Scope* originalScope = m_scope;
+ // Special scoping rules.
+ m_scope = m_info.scopes.at(&_forLoop.pre).get();
+
+ walkVector(_forLoop.pre.statements);
+ visit(*_forLoop.condition);
+ (*this)(_forLoop.body);
+ (*this)(_forLoop.post);
+
+ m_scope = originalScope;
+}
+
+
+void VariableReferenceCounter::operator()(Block const& _block)
+{
+ Scope* originalScope = m_scope;
+ m_scope = m_info.scopes.at(&_block).get();
+
+ ASTWalker::operator()(_block);
+
+ m_scope = originalScope;
+}
+
+void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
+{
+ m_scope->lookup(_variableName, Scope::Visitor(
+ [=](Scope::Variable const& _var)
+ {
+ ++m_context.variableReferences[&_var];
+ },
+ [=](Scope::Label const&) { },
+ [=](Scope::Function const&) { }
+ ));
+}
+
+
+CodeTransform::CodeTransform(
+ AbstractAssembly& _assembly,
+ AsmAnalysisInfo& _analysisInfo,
+ Block const& _block,
+ bool _allowStackOpt,
+ EVMDialect const& _dialect,
+ bool _evm15,
+ ExternalIdentifierAccess const& _identifierAccess,
+ bool _useNamedLabelsForFunctions,
+ int _stackAdjustment,
+ shared_ptr<Context> _context
+):
+ m_assembly(_assembly),
+ m_info(_analysisInfo),
+ m_dialect(_dialect),
+ m_allowStackOpt(_allowStackOpt),
+ m_evm15(_evm15),
+ m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
+ m_identifierAccess(_identifierAccess),
+ m_stackAdjustment(_stackAdjustment),
+ m_context(_context)
+{
+ if (!m_context)
+ {
+ // initialize
+ m_context = make_shared<Context>();
+ if (m_allowStackOpt)
+ VariableReferenceCounter{*m_context, m_info}(_block);
+ }
+}
+
+void CodeTransform::decreaseReference(YulString, Scope::Variable const& _var)
+{
+ if (!m_allowStackOpt)
+ return;
+
+ unsigned& ref = m_context->variableReferences.at(&_var);
+ solAssert(ref >= 1, "");
+ --ref;
+ if (ref == 0)
+ m_variablesScheduledForDeletion.insert(&_var);
+}
+
+bool CodeTransform::unreferenced(Scope::Variable const& _var) const
+{
+ return !m_context->variableReferences.count(&_var) || m_context->variableReferences[&_var] == 0;
+}
+
+void CodeTransform::freeUnusedVariables()
+{
+ if (!m_allowStackOpt)
+ return;
+
+ for (auto const& identifier: m_scope->identifiers)
+ if (identifier.second.type() == typeid(Scope::Variable))
+ {
+ Scope::Variable const& var = boost::get<Scope::Variable>(identifier.second);
+ if (m_variablesScheduledForDeletion.count(&var))
+ deleteVariable(var);
+ }
+
+ while (m_unusedStackSlots.count(m_assembly.stackHeight() - 1))
+ {
+ solAssert(m_unusedStackSlots.erase(m_assembly.stackHeight() - 1), "");
+ m_assembly.appendInstruction(solidity::Instruction::POP);
+ --m_stackAdjustment;
+ }
+}
+
+void CodeTransform::deleteVariable(Scope::Variable const& _var)
+{
+ solAssert(m_allowStackOpt, "");
+ solAssert(m_context->variableStackHeights.count(&_var) > 0, "");
+ m_unusedStackSlots.insert(m_context->variableStackHeights[&_var]);
+ m_context->variableStackHeights.erase(&_var);
+ m_context->variableReferences.erase(&_var);
+ m_variablesScheduledForDeletion.erase(&_var);
+}
+
void CodeTransform::operator()(VariableDeclaration const& _varDecl)
{
solAssert(m_scope, "");
@@ -49,10 +188,40 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
while (variablesLeft--)
m_assembly.appendConstant(u256(0));
}
- for (auto const& variable: _varDecl.variables)
+
+ bool atTopOfStack = true;
+ for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex)
{
- auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name));
- m_context->variableStackHeights[&var] = height++;
+ auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(_varDecl.variables[varIndex].name));
+ m_context->variableStackHeights[&var] = height + varIndex;
+ if (!m_allowStackOpt)
+ continue;
+
+ if (unreferenced(var))
+ {
+ if (atTopOfStack)
+ {
+ m_context->variableStackHeights.erase(&var);
+ m_assembly.setSourceLocation(_varDecl.location);
+ m_assembly.appendInstruction(solidity::Instruction::POP);
+ --m_stackAdjustment;
+ }
+ else
+ m_variablesScheduledForDeletion.insert(&var);
+ }
+ else if (m_unusedStackSlots.empty())
+ atTopOfStack = false;
+ else
+ {
+ int slot = *m_unusedStackSlots.begin();
+ m_unusedStackSlots.erase(m_unusedStackSlots.begin());
+ m_context->variableStackHeights[&var] = slot;
+ m_assembly.setSourceLocation(_varDecl.location);
+ if (int heightDiff = variableHeightDiff(var, true))
+ m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1));
+ m_assembly.appendInstruction(solidity::Instruction::POP);
+ --m_stackAdjustment;
+ }
}
checkStackHeight(&_varDecl);
}
@@ -70,6 +239,7 @@ void CodeTransform::operator()(Assignment const& _assignment)
void CodeTransform::operator()(StackAssignment const& _assignment)
{
+ solAssert(!m_allowStackOpt, "");
m_assembly.setSourceLocation(_assignment.location);
generateAssignment(_assignment.variableName);
checkStackHeight(&_assignment);
@@ -84,6 +254,7 @@ void CodeTransform::operator()(ExpressionStatement const& _statement)
void CodeTransform::operator()(Label const& _label)
{
+ solAssert(!m_allowStackOpt, "");
m_assembly.setSourceLocation(_label.location);
solAssert(m_scope, "");
solAssert(m_scope->identifiers.count(_label.name), "");
@@ -96,35 +267,46 @@ void CodeTransform::operator()(FunctionCall const& _call)
{
solAssert(m_scope, "");
- m_assembly.setSourceLocation(_call.location);
- EVMAssembly::LabelID returnLabel(-1); // only used for evm 1.0
- if (!m_evm15)
+ if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name))
{
- returnLabel = m_assembly.newLabelId();
- m_assembly.appendLabelReference(returnLabel);
- m_stackAdjustment++;
+ builtin->generateCode(_call, m_assembly, [&]() {
+ for (auto const& arg: _call.arguments | boost::adaptors::reversed)
+ visitExpression(arg);
+ m_assembly.setSourceLocation(_call.location);
+ });
}
-
- Scope::Function* function = nullptr;
- solAssert(m_scope->lookup(_call.functionName.name, Scope::NonconstVisitor(
- [=](Scope::Variable&) { solAssert(false, "Expected function name."); },
- [=](Scope::Label&) { solAssert(false, "Expected function name."); },
- [&](Scope::Function& _function) { function = &_function; }
- )), "Function name not found.");
- solAssert(function, "");
- solAssert(function->arguments.size() == _call.arguments.size(), "");
- for (auto const& arg: _call.arguments | boost::adaptors::reversed)
- visitExpression(arg);
- m_assembly.setSourceLocation(_call.location);
- if (m_evm15)
- m_assembly.appendJumpsub(functionEntryID(_call.functionName.name, *function), function->arguments.size(), function->returns.size());
else
{
- m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1);
- m_assembly.appendLabel(returnLabel);
- m_stackAdjustment--;
+ m_assembly.setSourceLocation(_call.location);
+ EVMAssembly::LabelID returnLabel(-1); // only used for evm 1.0
+ if (!m_evm15)
+ {
+ returnLabel = m_assembly.newLabelId();
+ m_assembly.appendLabelReference(returnLabel);
+ m_stackAdjustment++;
+ }
+
+ Scope::Function* function = nullptr;
+ solAssert(m_scope->lookup(_call.functionName.name, Scope::NonconstVisitor(
+ [=](Scope::Variable&) { solAssert(false, "Expected function name."); },
+ [=](Scope::Label&) { solAssert(false, "Expected function name."); },
+ [&](Scope::Function& _function) { function = &_function; }
+ )), "Function name not found.");
+ solAssert(function, "");
+ solAssert(function->arguments.size() == _call.arguments.size(), "");
+ for (auto const& arg: _call.arguments | boost::adaptors::reversed)
+ visitExpression(arg);
+ m_assembly.setSourceLocation(_call.location);
+ if (m_evm15)
+ m_assembly.appendJumpsub(functionEntryID(_call.functionName.name, *function), function->arguments.size(), function->returns.size());
+ else
+ {
+ m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1);
+ m_assembly.appendLabel(returnLabel);
+ m_stackAdjustment--;
+ }
+ checkStackHeight(&_call);
}
- checkStackHeight(&_call);
}
void CodeTransform::operator()(FunctionalInstruction const& _instruction)
@@ -169,11 +351,14 @@ void CodeTransform::operator()(Identifier const& _identifier)
if (m_scope->lookup(_identifier.name, Scope::NonconstVisitor(
[=](Scope::Variable& _var)
{
+ // TODO: opportunity for optimization: Do not DUP if this is the last reference
+ // to the top most element of the stack
if (int heightDiff = variableHeightDiff(_var, false))
m_assembly.appendInstruction(solidity::dupInstruction(heightDiff));
else
// Store something to balance the stack
m_assembly.appendConstant(u256(0));
+ decreaseReference(_identifier.name, _var);
},
[=](Scope::Label& _label)
{
@@ -202,7 +387,7 @@ void CodeTransform::operator()(Literal const& _literal)
m_assembly.appendConstant(u256(_literal.value.str()));
else if (_literal.kind == LiteralKind::Boolean)
{
- if (_literal.value.str() == "true")
+ if (_literal.value == "true"_yulstring)
m_assembly.appendConstant(u256(1));
else
m_assembly.appendConstant(u256(0));
@@ -217,6 +402,7 @@ void CodeTransform::operator()(Literal const& _literal)
void CodeTransform::operator()(yul::Instruction const& _instruction)
{
+ solAssert(!m_allowStackOpt, "");
solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5");
solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5");
m_assembly.setSourceLocation(_instruction.location);
@@ -329,7 +515,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
CodeTransform(
m_assembly,
m_info,
- m_yul,
+ _function.body,
+ m_allowStackOpt,
+ m_dialect,
m_evm15,
m_identifierAccess,
m_useNamedLabelsForFunctions,
@@ -350,6 +538,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
if (!m_evm15)
stackLayout.push_back(_function.returnVariables.size()); // Move return label to the top
stackLayout += vector<int>(_function.parameters.size(), -1); // discard all arguments
+
for (size_t i = 0; i < _function.returnVariables.size(); ++i)
stackLayout.push_back(i); // Move return values down, but keep order.
@@ -475,20 +664,37 @@ void CodeTransform::visitExpression(Expression const& _expression)
void CodeTransform::visitStatements(vector<Statement> const& _statements)
{
for (auto const& statement: _statements)
+ {
+ freeUnusedVariables();
boost::apply_visitor(*this, statement);
+ }
+ freeUnusedVariables();
}
void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight)
{
m_assembly.setSourceLocation(_block.location);
+ freeUnusedVariables();
+
// pop variables
solAssert(m_info.scopes.at(&_block).get() == m_scope, "");
- for (size_t i = 0; i < m_scope->numberOfVariables(); ++i)
- m_assembly.appendInstruction(solidity::Instruction::POP);
+ for (auto const& id: m_scope->identifiers)
+ if (id.second.type() == typeid(Scope::Variable))
+ {
+ Scope::Variable const& var = boost::get<Scope::Variable>(id.second);
+ if (m_allowStackOpt)
+ {
+ solAssert(!m_context->variableStackHeights.count(&var), "");
+ solAssert(!m_context->variableReferences.count(&var), "");
+ m_stackAdjustment++;
+ }
+ else
+ m_assembly.appendInstruction(solidity::Instruction::POP);
+ }
int deposit = m_assembly.stackHeight() - blockStartStackHeight;
- solAssert(deposit == 0, "Invalid stack height at end of block.");
+ solAssert(deposit == 0, "Invalid stack height at end of block: " + to_string(deposit));
checkStackHeight(&_block);
}
@@ -502,13 +708,13 @@ void CodeTransform::generateMultiAssignment(vector<Identifier> const& _variableN
void CodeTransform::generateAssignment(Identifier const& _variableName)
{
solAssert(m_scope, "");
- auto var = m_scope->lookup(_variableName.name);
- if (var)
+ if (auto var = m_scope->lookup(_variableName.name))
{
Scope::Variable const& _var = boost::get<Scope::Variable>(*var);
if (int heightDiff = variableHeightDiff(_var, true))
m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1));
m_assembly.appendInstruction(solidity::Instruction::POP);
+ decreaseReference(_variableName.name, _var);
}
else
{
diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h
index d559f85a..7be6f892 100644
--- a/libyul/backends/evm/EVMCodeTransform.h
+++ b/libyul/backends/evm/EVMCodeTransform.h
@@ -18,10 +18,13 @@
* Common code generator for translating Yul / inline assembly to EVM and EVM1.5.
*/
+#pragma once
+
#include <libyul/backends/evm/EVMAssembly.h>
+#include <libyul/backends/evm/EVMDialect.h>
+#include <libyul/optimiser/ASTWalker.h>
#include <libyul/AsmDataForward.h>
-
#include <libyul/AsmScope.h>
#include <boost/variant.hpp>
@@ -37,6 +40,46 @@ namespace yul
struct AsmAnalysisInfo;
class EVMAssembly;
+struct CodeTransformContext
+{
+ std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs;
+ std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
+ std::map<Scope::Variable const*, int> variableStackHeights;
+ std::map<Scope::Variable const*, unsigned> variableReferences;
+};
+
+/**
+ * Counts the number of references to a variable. This includes actual (read) references
+ * but also assignments to the variable. It does not include the declaration itself or
+ * function parameters, but it does include function return parameters.
+ *
+ * This component can handle multiple variables of the same name.
+ *
+ * Can only be applied to strict assembly.
+ */
+class VariableReferenceCounter: public yul::ASTWalker
+{
+public:
+ explicit VariableReferenceCounter(
+ CodeTransformContext& _context,
+ AsmAnalysisInfo const& _assemblyInfo
+ ): m_context(_context), m_info(_assemblyInfo)
+ {}
+
+public:
+ void operator()(Identifier const& _identifier);
+ void operator()(FunctionDefinition const&);
+ void operator()(ForLoop const&);
+ void operator()(Block const& _block);
+
+private:
+ void increaseRefIfFound(YulString _variableName);
+
+ CodeTransformContext& m_context;
+ AsmAnalysisInfo const& m_info;
+ Scope* m_scope = nullptr;
+};
+
class CodeTransform: public boost::static_visitor<>
{
public:
@@ -45,50 +88,51 @@ public:
CodeTransform(
AbstractAssembly& _assembly,
AsmAnalysisInfo& _analysisInfo,
- bool _yul = false,
+ Block const& _block,
+ EVMDialect const& _dialect,
+ bool _allowStackOpt = false,
bool _evm15 = false,
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(),
bool _useNamedLabelsForFunctions = false
): CodeTransform(
_assembly,
_analysisInfo,
- _yul,
+ _block,
+ _allowStackOpt,
+ _dialect,
_evm15,
_identifierAccess,
_useNamedLabelsForFunctions,
_assembly.stackHeight(),
- std::make_shared<Context>()
+ nullptr
)
{
}
protected:
- struct Context
- {
- std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs;
- std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs;
- std::map<Scope::Variable const*, int> variableStackHeights;
- };
+ using Context = CodeTransformContext;
CodeTransform(
AbstractAssembly& _assembly,
AsmAnalysisInfo& _analysisInfo,
- bool _yul,
+ Block const& _block,
+ bool _allowStackOpt,
+ EVMDialect const& _dialect,
bool _evm15,
ExternalIdentifierAccess const& _identifierAccess,
bool _useNamedLabelsForFunctions,
int _stackAdjustment,
std::shared_ptr<Context> _context
- ):
- m_assembly(_assembly),
- m_info(_analysisInfo),
- m_yul(_yul),
- m_evm15(_evm15),
- m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
- m_identifierAccess(_identifierAccess),
- m_stackAdjustment(_stackAdjustment),
- m_context(_context)
- {}
+ );
+
+ void decreaseReference(YulString _name, Scope::Variable const& _var);
+ bool unreferenced(Scope::Variable const& _var) const;
+ /// Marks slots of variables that are not used anymore
+ /// and were defined in the current scope for reuse.
+ /// Also POPs unused topmost stack slots.
+ void freeUnusedVariables();
+ /// Marks the stack slot of @a _var to be reused.
+ void deleteVariable(Scope::Variable const& _var);
public:
void operator()(Instruction const& _instruction);
@@ -137,9 +181,10 @@ private:
AbstractAssembly& m_assembly;
AsmAnalysisInfo& m_info;
Scope* m_scope = nullptr;
- bool m_yul = false;
- bool m_evm15 = false;
- bool m_useNamedLabelsForFunctions = false;
+ EVMDialect const& m_dialect;
+ bool const m_allowStackOpt = true;
+ bool const m_evm15 = false;
+ bool const m_useNamedLabelsForFunctions = false;
ExternalIdentifierAccess m_identifierAccess;
/// Adjustment between the stack height as determined during the analysis phase
/// and the stack height in the assembly. This is caused by an initial stack being present
@@ -147,6 +192,12 @@ private:
/// (EVM 1.0 or 1.5).
int m_stackAdjustment = 0;
std::shared_ptr<Context> m_context;
+
+ /// Set of variables whose reference counter has reached zero,
+ /// and whose stack slot will be marked as unused once we reach
+ /// statement level in the scope where the variable was defined.
+ std::set<Scope::Variable const*> m_variablesScheduledForDeletion;
+ std::set<int> m_unusedStackSlots;
};
}
diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp
new file mode 100644
index 00000000..935f05c6
--- /dev/null
+++ b/libyul/backends/evm/EVMDialect.cpp
@@ -0,0 +1,141 @@
+/*
+ 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/>.
+*/
+/**
+ * Yul dialects for EVM.
+ */
+
+#include <libyul/backends/evm/EVMDialect.h>
+
+#include <libyul/AsmAnalysisInfo.h>
+#include <libyul/AsmData.h>
+#include <libyul/Object.h>
+#include <libyul/backends/evm/AbstractAssembly.h>
+
+#include <liblangutil/Exceptions.h>
+
+#include <libyul/Exceptions.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+using namespace std;
+using namespace dev;
+using namespace yul;
+using namespace dev::solidity;
+
+
+EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess):
+ Dialect{_flavour}, m_objectAccess(_objectAccess)
+{
+ // The EVM instructions will be moved to builtins at some point.
+ if (!m_objectAccess)
+ return;
+
+ addFunction("datasize", 1, 1, true, [this](
+ FunctionCall const& _call,
+ AbstractAssembly& _assembly,
+ std::function<void()>
+ ) {
+ yulAssert(m_currentObject, "No object available.");
+ yulAssert(_call.arguments.size() == 1, "");
+ Expression const& arg = _call.arguments.front();
+ YulString dataName = boost::get<Literal>(arg).value;
+ if (m_currentObject->name == dataName)
+ _assembly.appendAssemblySize();
+ else
+ _assembly.appendDataSize(m_subIDs.at(dataName));
+ });
+ addFunction("dataoffset", 1, 1, true, [this](
+ FunctionCall const& _call,
+ AbstractAssembly& _assembly,
+ std::function<void()>
+ ) {
+ yulAssert(m_currentObject, "No object available.");
+ yulAssert(_call.arguments.size() == 1, "");
+ Expression const& arg = _call.arguments.front();
+ YulString dataName = boost::get<Literal>(arg).value;
+ if (m_currentObject->name == dataName)
+ _assembly.appendConstant(0);
+ else
+ _assembly.appendDataOffset(m_subIDs.at(dataName));
+ });
+ addFunction("datacopy", 3, 0, false, [](
+ FunctionCall const&,
+ AbstractAssembly& _assembly,
+ std::function<void()> _visitArguments
+ ) {
+ _visitArguments();
+ _assembly.appendInstruction(solidity::Instruction::CODECOPY);
+ });
+}
+
+BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const
+{
+ auto it = m_functions.find(_name);
+ if (it != m_functions.end())
+ return &it->second;
+ else
+ return nullptr;
+}
+
+shared_ptr<EVMDialect> EVMDialect::looseAssemblyForEVM()
+{
+ return make_shared<EVMDialect>(AsmFlavour::Loose, false);
+}
+
+shared_ptr<EVMDialect> EVMDialect::strictAssemblyForEVM()
+{
+ return make_shared<EVMDialect>(AsmFlavour::Strict, false);
+}
+
+shared_ptr<EVMDialect> EVMDialect::strictAssemblyForEVMObjects()
+{
+ return make_shared<EVMDialect>(AsmFlavour::Strict, true);
+}
+
+shared_ptr<yul::EVMDialect> EVMDialect::yulForEVM()
+{
+ return make_shared<EVMDialect>(AsmFlavour::Yul, false);
+}
+
+void EVMDialect::setSubIDs(map<YulString, AbstractAssembly::SubID> _subIDs)
+{
+ yulAssert(m_objectAccess, "Sub IDs set with dialect that does not support object access.");
+ m_subIDs = std::move(_subIDs);
+}
+
+void EVMDialect::setCurrentObject(Object const* _object)
+{
+ yulAssert(m_objectAccess, "Current object set with dialect that does not support object access.");
+ m_currentObject = _object;
+}
+
+void EVMDialect::addFunction(
+ string _name,
+ size_t _params,
+ size_t _returns,
+ bool _movable,
+ std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode
+)
+{
+ YulString name{std::move(_name)};
+ BuiltinFunctionForEVM& f = m_functions[name];
+ f.name = name;
+ f.parameters.resize(_params);
+ f.returns.resize(_returns);
+ f.movable = _movable;
+ f.generateCode = std::move(_generateCode);
+}
diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h
new file mode 100644
index 00000000..feb00b03
--- /dev/null
+++ b/libyul/backends/evm/EVMDialect.h
@@ -0,0 +1,85 @@
+/*
+ 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/>.
+*/
+/**
+ * Yul dialects for EVM.
+ */
+
+#pragma once
+
+#include <libyul/Dialect.h>
+
+#include <libyul/backends/evm/AbstractAssembly.h>
+
+#include <map>
+
+namespace yul
+{
+
+class YulString;
+using Type = YulString;
+struct FunctionCall;
+struct Object;
+
+struct BuiltinFunctionForEVM: BuiltinFunction
+{
+ /// Function to generate code for the given function call and append it to the abstract
+ /// assembly. The third parameter is called to visit (and generate code for) the arguments
+ /// from right to left.
+ std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> generateCode;
+};
+
+/**
+ * Yul dialect for EVM as a backend.
+ * The main difference is that the builtin functions take an AbstractAssembly for the
+ * code generation.
+ */
+struct EVMDialect: public Dialect
+{
+ EVMDialect(AsmFlavour _flavour, bool _objectAccess);
+
+ /// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
+ BuiltinFunctionForEVM const* builtin(YulString _name) const override;
+
+ static std::shared_ptr<EVMDialect> looseAssemblyForEVM();
+ static std::shared_ptr<EVMDialect> strictAssemblyForEVM();
+ static std::shared_ptr<EVMDialect> strictAssemblyForEVMObjects();
+ static std::shared_ptr<EVMDialect> yulForEVM();
+
+ bool providesObjectAccess() const { return m_objectAccess; }
+
+ /// Sets the mapping of current sub assembly IDs. Used during code generation.
+ void setSubIDs(std::map<YulString, AbstractAssembly::SubID> _subIDs);
+ /// Sets the current object. Used during code generation.
+ void setCurrentObject(Object const* _object);
+
+private:
+ void addFunction(
+ std::string _name,
+ size_t _params,
+ size_t _returns,
+ bool _movable,
+ std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode
+ );
+
+ bool m_objectAccess;
+ Object const* m_currentObject = nullptr;
+ /// Mapping from named objects to abstract assembly sub IDs.
+ std::map<YulString, AbstractAssembly::SubID> m_subIDs;
+ std::map<YulString, BuiltinFunctionForEVM> m_functions;
+};
+
+}
diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp
new file mode 100644
index 00000000..3f7634b2
--- /dev/null
+++ b/libyul/backends/evm/EVMObjectCompiler.cpp
@@ -0,0 +1,64 @@
+/*
+ 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/>.
+*/
+/**
+ * Compiler that transforms Yul Objects to EVM bytecode objects.
+ */
+
+#include <libyul/backends/evm/EVMObjectCompiler.h>
+
+#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/backends/evm/EVMDialect.h>
+
+#include <libyul/Object.h>
+#include <libyul/Exceptions.h>
+
+using namespace yul;
+using namespace std;
+
+void EVMObjectCompiler::compile(Object& _object, AbstractAssembly& _assembly, EVMDialect& _dialect, bool _evm15, bool _optimize)
+{
+ EVMObjectCompiler compiler(_assembly, _dialect, _evm15);
+ compiler.run(_object, _optimize);
+}
+
+void EVMObjectCompiler::run(Object& _object, bool _optimize)
+{
+ map<YulString, AbstractAssembly::SubID> subIDs;
+
+ for (auto& subNode: _object.subObjects)
+ if (Object* subObject = dynamic_cast<Object*>(subNode.get()))
+ {
+ auto subAssemblyAndID = m_assembly.createSubAssembly();
+ subIDs[subObject->name] = subAssemblyAndID.second;
+ compile(*subObject, *subAssemblyAndID.first, m_dialect, m_evm15, _optimize);
+ }
+ else
+ {
+ Data const& data = dynamic_cast<Data const&>(*subNode);
+ subIDs[data.name] = m_assembly.appendData(data.data);
+ }
+
+ if (m_dialect.providesObjectAccess())
+ {
+ m_dialect.setSubIDs(std::move(subIDs));
+ m_dialect.setCurrentObject(&_object);
+ }
+
+ yulAssert(_object.analysisInfo, "No analysis info.");
+ yulAssert(_object.code, "No code.");
+ CodeTransform{m_assembly, *_object.analysisInfo, *_object.code, m_dialect, _optimize, m_evm15}(*_object.code);
+}
diff --git a/libyul/AsmCodeGen.h b/libyul/backends/evm/EVMObjectCompiler.h
index fd5ac0a1..9325e072 100644
--- a/libyul/AsmCodeGen.h
+++ b/libyul/backends/evm/EVMObjectCompiler.h
@@ -15,40 +15,31 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
- * @author Christian <c@ethdev.com>
- * @date 2016
- * Code-generating part of inline assembly.
+ * Compiler that transforms Yul Objects to EVM bytecode objects.
*/
#pragma once
-#include <libyul/AsmAnalysis.h>
-
-#include <functional>
-
-namespace dev
-{
-namespace eth
-{
-class Assembly;
-}
-}
-
namespace yul
{
-struct Block;
+struct Object;
+class AbstractAssembly;
+struct EVMDialect;
-class CodeGenerator
+class EVMObjectCompiler
{
public:
- /// Performs code generation and appends generated to _assembly.
- static void assemble(
- Block const& _parsedData,
- AsmAnalysisInfo& _analysisInfo,
- dev::eth::Assembly& _assembly,
- yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(),
- bool _useNamedLabelsForFunctions = false
- );
+ static void compile(Object& _object, AbstractAssembly& _assembly, EVMDialect& _dialect, bool _evm15, bool _optimize);
+private:
+ EVMObjectCompiler(AbstractAssembly& _assembly, EVMDialect& _dialect, bool _evm15):
+ m_assembly(_assembly), m_dialect(_dialect), m_evm15(_evm15)
+ {}
+
+ void run(Object& _object, bool _optimize);
+
+ AbstractAssembly& m_assembly;
+ EVMDialect& m_dialect;
+ bool m_evm15 = false;
};
}
diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp
index 0d568007..6adcb2e1 100644
--- a/libyul/optimiser/ASTWalker.cpp
+++ b/libyul/optimiser/ASTWalker.cpp
@@ -93,6 +93,16 @@ void ASTWalker::operator()(Block const& _block)
walkVector(_block.statements);
}
+void ASTWalker::visit(Statement const& _st)
+{
+ boost::apply_visitor(*this, _st);
+}
+
+void ASTWalker::visit(Expression const& _e)
+{
+ boost::apply_visitor(*this, _e);
+}
+
void ASTModifier::operator()(FunctionalInstruction& _instr)
{
walkVector(_instr.arguments | boost::adaptors::reversed);
@@ -155,3 +165,13 @@ void ASTModifier::operator()(Block& _block)
{
walkVector(_block.statements);
}
+
+void ASTModifier::visit(Statement& _st)
+{
+ boost::apply_visitor(*this, _st);
+}
+
+void ASTModifier::visit(Expression& _e)
+{
+ boost::apply_visitor(*this, _e);
+}
diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h
index b59b405e..272725f9 100644
--- a/libyul/optimiser/ASTWalker.h
+++ b/libyul/optimiser/ASTWalker.h
@@ -58,14 +58,8 @@ public:
virtual void operator()(ForLoop const&);
virtual void operator()(Block const& _block);
- virtual void visit(Statement const& _st)
- {
- boost::apply_visitor(*this, _st);
- }
- virtual void visit(Expression const& _e)
- {
- boost::apply_visitor(*this, _e);
- }
+ virtual void visit(Statement const& _st);
+ virtual void visit(Expression const& _e);
protected:
template <class T>
@@ -99,14 +93,8 @@ public:
virtual void operator()(ForLoop&);
virtual void operator()(Block& _block);
- virtual void visit(Statement& _st)
- {
- boost::apply_visitor(*this, _st);
- }
- virtual void visit(Expression& _e)
- {
- boost::apply_visitor(*this, _e);
- }
+ virtual void visit(Statement& _st);
+ virtual void visit(Expression& _e);
protected:
template <class T>
diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp
index 64c67b38..c8d236dc 100644
--- a/libyul/optimiser/DataFlowAnalyzer.cpp
+++ b/libyul/optimiser/DataFlowAnalyzer.cpp
@@ -96,7 +96,10 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
for (auto const& parameter: _fun.parameters)
m_variableScopes.back().variables.emplace(parameter.name);
for (auto const& var: _fun.returnVariables)
+ {
m_variableScopes.back().variables.emplace(var.name);
+ handleAssignment({var.name}, nullptr);
+ }
ASTModifier::operator()(_fun);
popScope();
@@ -136,17 +139,22 @@ void DataFlowAnalyzer::operator()(Block& _block)
void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value)
{
+ static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
clearValues(_variables);
MovableChecker movableChecker;
if (_value)
movableChecker.visit(*_value);
- if (_variables.size() == 1)
+ else
+ for (auto const& var: _variables)
+ m_value[var] = &zero;
+
+ if (_value && _variables.size() == 1)
{
YulString name = *_variables.begin();
// Expression has to be movable and cannot contain a reference
// to the variable that will be assigned to.
- if (_value && movableChecker.movable() && !movableChecker.referencedVariables().count(name))
+ if (movableChecker.movable() && !movableChecker.referencedVariables().count(name))
m_value[name] = _value;
}
diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h
index cd134d48..4f12ff6a 100644
--- a/libyul/optimiser/DataFlowAnalyzer.h
+++ b/libyul/optimiser/DataFlowAnalyzer.h
@@ -36,6 +36,8 @@ namespace yul
* Tracks assignments and is used as base class for both Rematerialiser and
* Common Subexpression Eliminator.
*
+ * A special zero constant expression is used for the default value of variables.
+ *
* Prerequisite: Disambiguator
*/
class DataFlowAnalyzer: public ASTModifier
diff --git a/libyul/optimiser/ForLoopInitRewriter.cpp b/libyul/optimiser/ForLoopInitRewriter.cpp
index 80d39248..36d5db68 100644
--- a/libyul/optimiser/ForLoopInitRewriter.cpp
+++ b/libyul/optimiser/ForLoopInitRewriter.cpp
@@ -27,17 +27,24 @@ void ForLoopInitRewriter::operator()(Block& _block)
{
iterateReplacing(
_block.statements,
- [](Statement& _stmt) -> boost::optional<vector<Statement>>
+ [&](Statement& _stmt) -> boost::optional<vector<Statement>>
{
if (_stmt.type() == typeid(ForLoop))
{
auto& forLoop = boost::get<ForLoop>(_stmt);
+ (*this)(forLoop.pre);
+ (*this)(forLoop.body);
+ (*this)(forLoop.post);
vector<Statement> rewrite;
swap(rewrite, forLoop.pre.statements);
rewrite.emplace_back(move(forLoop));
return rewrite;
}
- return {};
+ else
+ {
+ visit(_stmt);
+ return {};
+ }
}
);
}
diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp
index 8ae26fbb..95360dc3 100644
--- a/libyul/optimiser/FullInliner.cpp
+++ b/libyul/optimiser/FullInliner.cpp
@@ -49,6 +49,8 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
if (ssaValue.second && ssaValue.second->type() == typeid(Literal))
m_constants.emplace(ssaValue.first);
+ // Store size of global statements.
+ m_functionSizes[YulString{}] = CodeSize::codeSize(_ast);
map<YulString, size_t> references = ReferencesCounter::countReferences(m_ast);
for (auto& statement: m_ast.statements)
{
@@ -58,7 +60,7 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
m_functions[fun.name] = &fun;
// Always inline functions that are only called once.
if (references[fun.name] == 1)
- m_alwaysInline.emplace(fun.name);
+ m_singleUse.emplace(fun.name);
updateCodeSize(fun);
}
}
@@ -94,8 +96,15 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
if (_funCall.functionName.name == _callSite)
return false;
- FunctionDefinition& calledFunction = function(_funCall.functionName.name);
- if (m_alwaysInline.count(calledFunction.name))
+ FunctionDefinition* calledFunction = function(_funCall.functionName.name);
+ if (!calledFunction)
+ return false;
+
+ // Do not inline into already big functions.
+ if (m_functionSizes.at(_callSite) > 100)
+ return false;
+
+ if (m_singleUse.count(calledFunction->name))
return true;
// Constant arguments might provide a means for further optimization, so they cause a bonus.
@@ -110,8 +119,13 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
break;
}
- size_t size = m_functionSizes.at(calledFunction.name);
- return (size < 10 || (constantArg && size < 50));
+ size_t size = m_functionSizes.at(calledFunction->name);
+ return (size < 10 || (constantArg && size < 30));
+}
+
+void FullInliner::tentativelyUpdateCodeSize(YulString _function, YulString _callSite)
+{
+ m_functionSizes.at(_callSite) += m_functionSizes.at(_function);
}
@@ -149,25 +163,32 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC
vector<Statement> newStatements;
map<YulString, YulString> variableReplacements;
- FunctionDefinition& function = m_driver.function(_funCall.functionName.name);
+ FunctionDefinition* function = m_driver.function(_funCall.functionName.name);
+ assertThrow(!!function, OptimizerException, "Attempt to inline invalid function.");
+
+ m_driver.tentativelyUpdateCodeSize(function->name, m_currentFunction);
+
+ static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
// helper function to create a new variable that is supposed to model
// an existing variable.
auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) {
- YulString newName = m_nameDispenser.newName(_existingVariable.name, function.name);
+ YulString newName = m_nameDispenser.newName(_existingVariable.name, function->name);
variableReplacements[_existingVariable.name] = newName;
VariableDeclaration varDecl{_funCall.location, {{_funCall.location, newName, _existingVariable.type}}, {}};
if (_value)
varDecl.value = make_shared<Expression>(std::move(*_value));
+ else
+ varDecl.value = make_shared<Expression>(zero);
newStatements.emplace_back(std::move(varDecl));
};
for (size_t i = 0; i < _funCall.arguments.size(); ++i)
- newVariable(function.parameters[i], &_funCall.arguments[i]);
- for (auto const& var: function.returnVariables)
+ newVariable(function->parameters[i], &_funCall.arguments[i]);
+ for (auto const& var: function->returnVariables)
newVariable(var, nullptr);
- Statement newBody = BodyCopier(m_nameDispenser, function.name, variableReplacements)(function.body);
+ Statement newBody = BodyCopier(m_nameDispenser, function->name, variableReplacements)(function->body);
newStatements += std::move(boost::get<Block>(newBody).statements);
boost::apply_visitor(GenericFallbackVisitor<Assignment, VariableDeclaration>{
@@ -179,7 +200,7 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC
{_assignment.variableNames[i]},
make_shared<Expression>(Identifier{
_assignment.location,
- variableReplacements.at(function.returnVariables[i].name)
+ variableReplacements.at(function->returnVariables[i].name)
})
});
},
@@ -191,7 +212,7 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC
{std::move(_varDecl.variables[i])},
make_shared<Expression>(Identifier{
_varDecl.location,
- variableReplacements.at(function.returnVariables[i].name)
+ variableReplacements.at(function->returnVariables[i].name)
})
});
}
diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h
index a8fe76c6..d2dd3229 100644
--- a/libyul/optimiser/FullInliner.h
+++ b/libyul/optimiser/FullInliner.h
@@ -63,8 +63,8 @@ class NameCollector;
* code of f, with replacements: a -> f_a, b -> f_b, c -> f_c
* let z := f_c
*
- * Prerequisites: Disambiguator, Function Hoister
- * More efficient if run after: Expression Splitter
+ * Prerequisites: Disambiguator
+ * More efficient if run after: Function Hoister, Expression Splitter
*/
class FullInliner: public ASTModifier
{
@@ -77,7 +77,18 @@ public:
/// @param _callSite the name of the function in which the function call is located.
bool shallInline(FunctionCall const& _funCall, YulString _callSite);
- FunctionDefinition& function(YulString _name) { return *m_functions.at(_name); }
+ FunctionDefinition* function(YulString _name)
+ {
+ auto it = m_functions.find(_name);
+ if (it != m_functions.end())
+ return it->second;
+ return nullptr;
+ }
+
+ /// Adds the size of _funCall to the size of _callSite. This is just
+ /// a rough estimate that is done during inlining. The proper size
+ /// should be determined after inlining is completed.
+ void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
private:
void updateCodeSize(FunctionDefinition& fun);
@@ -88,7 +99,7 @@ private:
Block& m_ast;
std::map<YulString, FunctionDefinition*> m_functions;
/// Names of functions to always inline.
- std::set<YulString> m_alwaysInline;
+ std::set<YulString> m_singleUse;
/// Variables that are constants (used for inlining heuristic)
std::set<YulString> m_constants;
std::map<YulString, size_t> m_functionSizes;
diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp
index 63eea2db..fabbf66f 100644
--- a/libyul/optimiser/MainFunction.cpp
+++ b/libyul/optimiser/MainFunction.cpp
@@ -40,12 +40,12 @@ void MainFunction::operator()(Block& _block)
for (size_t i = 1; i < _block.statements.size(); ++i)
assertThrow(_block.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, "");
/// @todo this should handle scopes properly and instead of an assertion it should rename the conflicting function
- assertThrow(NameCollector(_block).names().count(YulString{"main"}) == 0, OptimizerException, "");
+ assertThrow(NameCollector(_block).names().count("main"_yulstring) == 0, OptimizerException, "");
Block& block = boost::get<Block>(_block.statements[0]);
FunctionDefinition main{
block.location,
- YulString{"main"},
+ "main"_yulstring,
{},
{},
std::move(block)
diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp
index a5557fb3..8fc9476e 100644
--- a/libyul/optimiser/Metrics.cpp
+++ b/libyul/optimiser/Metrics.cpp
@@ -48,6 +48,9 @@ size_t CodeSize::codeSize(Block const& _block)
void CodeSize::visit(Statement const& _statement)
{
+ if (_statement.type() == typeid(FunctionDefinition))
+ return;
+
++m_size;
ASTWalker::visit(_statement);
}
diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h
index ca244600..d26ecbd9 100644
--- a/libyul/optimiser/Metrics.h
+++ b/libyul/optimiser/Metrics.h
@@ -25,17 +25,17 @@
namespace yul
{
+/**
+ * Metric for the size of code.
+ * More specifically, the number of AST nodes.
+ * Ignores function definitions while traversing the AST.
+ * If you want to know the size of a function, you have to invoke this on its body.
+ */
class CodeSize: public ASTWalker
{
public:
- /// Returns a metric for the code size of an AST element.
- /// More specifically, it returns the number of AST nodes.
static size_t codeSize(Statement const& _statement);
- /// Returns a metric for the code size of an AST element.
- /// More specifically, it returns the number of AST nodes.
static size_t codeSize(Expression const& _expression);
- /// Returns a metric for the code size of an AST element.
- /// More specifically, it returns the number of AST nodes.
static size_t codeSize(Block const& _block);
private:
diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h
index 54d65823..4f82e7a2 100644
--- a/libyul/optimiser/RedundantAssignEliminator.h
+++ b/libyul/optimiser/RedundantAssignEliminator.h
@@ -115,7 +115,7 @@ public:
static void run(Block& _ast);
private:
- RedundantAssignEliminator() {}
+ RedundantAssignEliminator() = default;
class State
{
diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp
index 35b29b04..23eb9ec2 100644
--- a/libyul/optimiser/SSAValueTracker.cpp
+++ b/libyul/optimiser/SSAValueTracker.cpp
@@ -33,13 +33,20 @@ void SSAValueTracker::operator()(Assignment const& _assignment)
m_values.erase(var.name);
}
+void SSAValueTracker::operator()(FunctionDefinition const& _funDef)
+{
+ for (auto const& var: _funDef.returnVariables)
+ setValue(var.name, nullptr);
+ ASTWalker::operator()(_funDef);
+}
+
void SSAValueTracker::operator()(VariableDeclaration const& _varDecl)
{
- if (_varDecl.variables.size() == 1)
- setValue(_varDecl.variables.front().name, _varDecl.value.get());
- else
+ if (!_varDecl.value)
for (auto const& var: _varDecl.variables)
setValue(var.name, nullptr);
+ else if (_varDecl.variables.size() == 1)
+ setValue(_varDecl.variables.front().name, _varDecl.value.get());
}
void SSAValueTracker::setValue(YulString _name, Expression const* _value)
@@ -49,5 +56,8 @@ void SSAValueTracker::setValue(YulString _name, Expression const* _value)
OptimizerException,
"Source needs to be disambiguated."
);
+ static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
+ if (!_value)
+ _value = &zero;
m_values[_name] = _value;
}
diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h
index e182e013..0680485f 100644
--- a/libyul/optimiser/SSAValueTracker.h
+++ b/libyul/optimiser/SSAValueTracker.h
@@ -33,12 +33,15 @@ namespace yul
* Class that walks the AST and stores the initial value of each variable
* that is never assigned to.
*
+ * A special zero constant expression is used for the default value of variables.
+ *
* Prerequisite: Disambiguator
*/
class SSAValueTracker: public ASTWalker
{
public:
using ASTWalker::operator();
+ void operator()(FunctionDefinition const& _funDef) override;
void operator()(VariableDeclaration const& _varDecl) override;
void operator()(Assignment const& _assignment) override;
diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp
index b3190fef..45b0ca2c 100644
--- a/libyul/optimiser/SimplificationRules.cpp
+++ b/libyul/optimiser/SimplificationRules.cpp
@@ -114,7 +114,8 @@ bool Pattern::matches(Expression const& _expr, map<YulString, Expression const*>
{
YulString varName = boost::get<Identifier>(_expr).name;
if (_ssaValues.count(varName))
- expr = _ssaValues.at(varName);
+ if (Expression const* new_expr = _ssaValues.at(varName))
+ expr = new_expr;
}
assertThrow(expr, OptimizerException, "");
@@ -207,10 +208,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const
u256 Pattern::d() const
{
- Literal const& literal = boost::get<Literal>(matchGroupValue());
- assertThrow(literal.kind == LiteralKind::Number, OptimizerException, "");
- assertThrow(isValidDecimal(literal.value.str()) || isValidHex(literal.value.str()), OptimizerException, "");
- return u256(literal.value.str());
+ return valueOfNumberLiteral(boost::get<Literal>(matchGroupValue()));
}
Expression const& Pattern::matchGroupValue() const
diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp
new file mode 100644
index 00000000..bdf4cb2a
--- /dev/null
+++ b/libyul/optimiser/StructuralSimplifier.cpp
@@ -0,0 +1,138 @@
+/*
+ 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 <libyul/optimiser/StructuralSimplifier.h>
+#include <libyul/optimiser/Semantics.h>
+#include <libyul/optimiser/Utilities.h>
+#include <libyul/AsmData.h>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/Visitor.h>
+
+using namespace std;
+using namespace dev;
+using namespace yul;
+
+namespace {
+
+ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression)
+{
+ return {_location, FunctionalInstruction{
+ _location,
+ solidity::Instruction::POP,
+ {std::move(_expression)}
+ }};
+}
+
+}
+
+void StructuralSimplifier::operator()(Block& _block)
+{
+ pushScope(false);
+ simplify(_block.statements);
+ popScope();
+}
+
+void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements)
+{
+ using OptionalStatements = boost::optional<vector<Statement>>;
+ GenericFallbackReturnsVisitor<OptionalStatements, If, Switch, ForLoop> const visitor(
+ [&](If& _ifStmt) -> OptionalStatements {
+ if (_ifStmt.body.statements.empty())
+ return {{makePopExpressionStatement(_ifStmt.location, std::move(*_ifStmt.condition))}};
+ if (expressionAlwaysTrue(*_ifStmt.condition))
+ return {std::move(_ifStmt.body.statements)};
+ else if (expressionAlwaysFalse(*_ifStmt.condition))
+ return {vector<Statement>{}};
+ return {};
+ },
+ [](Switch& _switchStmt) -> OptionalStatements {
+ if (_switchStmt.cases.size() == 1)
+ {
+ auto& switchCase = _switchStmt.cases.front();
+ auto loc = locationOf(*_switchStmt.expression);
+ if (switchCase.value)
+ return {{If{
+ std::move(_switchStmt.location),
+ make_shared<Expression>(FunctionalInstruction{
+ std::move(loc),
+ solidity::Instruction::EQ,
+ {std::move(*switchCase.value), std::move(*_switchStmt.expression)}
+ }), std::move(switchCase.body)}}};
+ else
+ return {{
+ makePopExpressionStatement(loc, std::move(*_switchStmt.expression)),
+ std::move(switchCase.body)
+ }};
+ }
+ else
+ return {};
+ },
+ [&](ForLoop& _forLoop) -> OptionalStatements {
+ if (expressionAlwaysFalse(*_forLoop.condition))
+ return {std::move(_forLoop.pre.statements)};
+ else
+ return {};
+ }
+ );
+
+ iterateReplacing(
+ _statements,
+ [&](Statement& _stmt) -> OptionalStatements
+ {
+ visit(_stmt);
+ OptionalStatements result = boost::apply_visitor(visitor, _stmt);
+ if (result)
+ simplify(*result);
+ return result;
+ }
+ );
+}
+
+bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression)
+{
+ return boost::apply_visitor(GenericFallbackReturnsVisitor<bool, Identifier const, Literal const>(
+ [&](Identifier const& _identifier) -> bool {
+ if (auto expr = m_value[_identifier.name])
+ return expressionAlwaysTrue(*expr);
+ return false;
+ },
+ [](Literal const& _literal) -> bool {
+ static YulString const trueString("true");
+ return
+ (_literal.kind == LiteralKind::Boolean && _literal.value == trueString) ||
+ (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) != u256(0))
+ ;
+ }
+ ), _expression);
+}
+
+bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression)
+{
+ return boost::apply_visitor(GenericFallbackReturnsVisitor<bool, Identifier const, Literal const>(
+ [&](Identifier const& _identifier) -> bool {
+ if (auto expr = m_value[_identifier.name])
+ return expressionAlwaysFalse(*expr);
+ return false;
+ },
+ [](Literal const& _literal) -> bool {
+ static YulString const falseString("false");
+ return
+ (_literal.kind == LiteralKind::Boolean && _literal.value == falseString) ||
+ (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) == u256(0))
+ ;
+ }
+ ), _expression);
+}
diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h
new file mode 100644
index 00000000..bbd8e005
--- /dev/null
+++ b/libyul/optimiser/StructuralSimplifier.h
@@ -0,0 +1,49 @@
+/*
+ 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 <libyul/optimiser/ASTWalker.h>
+#include <libyul/optimiser/DataFlowAnalyzer.h>
+
+namespace yul
+{
+
+/**
+ * Structural simplifier. Performs the following simplification steps:
+ * - replace if with empty body with pop(condition)
+ * - replace if with true condition with its body
+ * - remove if with false condition
+ * - turn switch with single case into if
+ * - replace switch with only default case with pop(expression) and body
+ * - remove for with false condition
+ *
+ * Prerequisites: Disambiguator
+ *
+ * Important: Can only be used on EVM code.
+ */
+class StructuralSimplifier: public DataFlowAnalyzer
+{
+public:
+ using DataFlowAnalyzer::operator();
+ void operator()(Block& _block) override;
+private:
+ void simplify(std::vector<Statement>& _statements);
+ bool expressionAlwaysTrue(Expression const &_expression);
+ bool expressionAlwaysFalse(Expression const &_expression);
+};
+
+}
diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp
index 36f0e1eb..bfba8dfc 100644
--- a/libyul/optimiser/Suite.cpp
+++ b/libyul/optimiser/Suite.cpp
@@ -21,6 +21,7 @@
#include <libyul/optimiser/Suite.h>
#include <libyul/optimiser/Disambiguator.h>
+#include <libyul/optimiser/VarDeclInitializer.h>
#include <libyul/optimiser/FunctionGrouper.h>
#include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/ExpressionSplitter.h>
@@ -33,8 +34,8 @@
#include <libyul/optimiser/ExpressionSimplifier.h>
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
#include <libyul/optimiser/SSATransform.h>
+#include <libyul/optimiser/StructuralSimplifier.h>
#include <libyul/optimiser/RedundantAssignEliminator.h>
-#include <libyul/optimiser/VarDeclPropagator.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
#include <libyul/AsmPrinter.h>
@@ -55,9 +56,11 @@ void OptimiserSuite::run(
Block ast = boost::get<Block>(Disambiguator(_analysisInfo, reservedIdentifiers)(_ast));
+ (VarDeclInitializer{})(ast);
(FunctionHoister{})(ast);
(FunctionGrouper{})(ast);
(ForLoopInitRewriter{})(ast);
+ StructuralSimplifier{}(ast);
NameDispenser dispenser{ast};
@@ -66,11 +69,11 @@ void OptimiserSuite::run(
ExpressionSplitter{dispenser}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(ast);
- VarDeclPropagator{}(ast);
RedundantAssignEliminator::run(ast);
CommonSubexpressionEliminator{}(ast);
ExpressionSimplifier::run(ast);
+ StructuralSimplifier{}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(ast);
RedundantAssignEliminator::run(ast);
@@ -92,26 +95,22 @@ void OptimiserSuite::run(
RedundantAssignEliminator::run(ast);
CommonSubexpressionEliminator{}(ast);
FullInliner{ast, dispenser}.run();
- VarDeclPropagator{}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(ast);
- VarDeclPropagator{}(ast);
RedundantAssignEliminator::run(ast);
ExpressionSimplifier::run(ast);
+ StructuralSimplifier{}(ast);
CommonSubexpressionEliminator{}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(ast);
- VarDeclPropagator{}(ast);
RedundantAssignEliminator::run(ast);
UnusedPruner::runUntilStabilised(ast, reservedIdentifiers);
}
ExpressionJoiner::run(ast);
- VarDeclPropagator{}(ast);
UnusedPruner::runUntilStabilised(ast);
ExpressionJoiner::run(ast);
UnusedPruner::runUntilStabilised(ast);
ExpressionJoiner::run(ast);
- VarDeclPropagator{}(ast);
UnusedPruner::runUntilStabilised(ast);
ExpressionJoiner::run(ast);
UnusedPruner::runUntilStabilised(ast);
diff --git a/libyul/optimiser/Utilities.cpp b/libyul/optimiser/Utilities.cpp
index b8cdd339..b3b580d5 100644
--- a/libyul/optimiser/Utilities.cpp
+++ b/libyul/optimiser/Utilities.cpp
@@ -21,6 +21,7 @@
#include <libyul/optimiser/Utilities.h>
#include <libyul/AsmData.h>
+#include <libyul/Exceptions.h>
#include <libdevcore/CommonData.h>
@@ -37,3 +38,11 @@ void yul::removeEmptyBlocks(Block& _block)
};
boost::range::remove_erase_if(_block.statements, isEmptyBlock);
}
+
+u256 yul::valueOfNumberLiteral(Literal const& _literal)
+{
+ assertThrow(_literal.kind == LiteralKind::Number, OptimizerException, "");
+ std::string const& literalString = _literal.value.str();
+ assertThrow(isValidDecimal(literalString) || isValidHex(literalString), OptimizerException, "");
+ return u256(literalString);
+}
diff --git a/libyul/optimiser/Utilities.h b/libyul/optimiser/Utilities.h
index c543b119..1cfff62b 100644
--- a/libyul/optimiser/Utilities.h
+++ b/libyul/optimiser/Utilities.h
@@ -20,6 +20,7 @@
#pragma once
+#include <libdevcore/Common.h>
#include <libyul/AsmDataForward.h>
namespace yul
@@ -28,4 +29,6 @@ namespace yul
/// Removes statements that are just empty blocks (non-recursive).
void removeEmptyBlocks(Block& _block);
+dev::u256 valueOfNumberLiteral(Literal const& _literal);
+
}
diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp
new file mode 100644
index 00000000..4a26757f
--- /dev/null
+++ b/libyul/optimiser/VarDeclInitializer.cpp
@@ -0,0 +1,56 @@
+/*
+ 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 <libyul/optimiser/VarDeclInitializer.h>
+#include <libyul/AsmData.h>
+
+#include <libdevcore/CommonData.h>
+#include <libdevcore/Visitor.h>
+
+using namespace std;
+using namespace dev;
+using namespace yul;
+
+void VarDeclInitializer::operator()(Block& _block)
+{
+ ASTModifier::operator()(_block);
+
+ static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
+
+ using OptionalStatements = boost::optional<vector<Statement>>;
+ GenericFallbackReturnsVisitor<OptionalStatements, VariableDeclaration> visitor{
+ [](VariableDeclaration& _varDecl) -> OptionalStatements
+ {
+ if (_varDecl.value)
+ return {};
+ else if (_varDecl.variables.size() == 1)
+ {
+ _varDecl.value = make_shared<Expression>(zero);
+ return {};
+ }
+ else
+ {
+ OptionalStatements ret{vector<Statement>{}};
+ langutil::SourceLocation loc{std::move(_varDecl.location)};
+ for (auto& var: _varDecl.variables)
+ ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_shared<Expression>(zero)});
+ return ret;
+ }
+ }
+ };
+ iterateReplacing(_block.statements, boost::apply_visitor(visitor));
+}
diff --git a/libyul/optimiser/VarDeclInitializer.h b/libyul/optimiser/VarDeclInitializer.h
new file mode 100644
index 00000000..41d0917c
--- /dev/null
+++ b/libyul/optimiser/VarDeclInitializer.h
@@ -0,0 +1,38 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <libyul/AsmDataForward.h>
+#include <libyul/optimiser/ASTWalker.h>
+
+namespace yul
+{
+
+/**
+ * Rewrites variable declarations so that all of them are initialized.
+ * Declarations like ``let x, y`` are split into multiple declaration
+ * statements.
+ * Only supports initializing with the zero literal for now.
+ */
+class VarDeclInitializer: public ASTModifier
+{
+public:
+ void operator()(Block& _block) override;
+};
+
+}
diff --git a/libyul/optimiser/VarDeclPropagator.cpp b/libyul/optimiser/VarDeclPropagator.cpp
deleted file mode 100644
index bf974f44..00000000
--- a/libyul/optimiser/VarDeclPropagator.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- 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 <libyul/optimiser/VarDeclPropagator.h>
-#include <libyul/AsmData.h>
-#include <libdevcore/CommonData.h>
-#include <boost/range/algorithm_ext/erase.hpp>
-#include <algorithm>
-#include <map>
-
-using namespace std;
-using namespace dev;
-using namespace yul;
-
-void VarDeclPropagator::operator()(Block& _block)
-{
- map<YulString, TypedName> outerEmptyVarDecls;
- map<YulString, TypedName> outerLazyInitializedVarDecls;
- swap(m_emptyVarDecls, outerEmptyVarDecls);
- swap(m_lazyInitializedVarDecls, outerLazyInitializedVarDecls);
-
- ASTModifier::operator()(_block);
-
- iterateReplacing(
- _block.statements,
- [this](Statement& _stmt) -> boost::optional<vector<Statement>>
- {
- if (_stmt.type() == typeid(VariableDeclaration))
- {
- VariableDeclaration& varDecl = boost::get<VariableDeclaration>(_stmt);
- boost::remove_erase_if(
- varDecl.variables,
- [&](TypedName const& _typedName) { return m_lazyInitializedVarDecls.count(_typedName.name); }
- );
- if (varDecl.variables.empty())
- return vector<Statement>{};
- else
- return {};
- }
- else if (_stmt.type() == typeid(Assignment))
- {
- Assignment& assignment = boost::get<Assignment>(_stmt);
- if (isFullyLazyInitialized(assignment.variableNames))
- return vector<Statement>{recreateVariableDeclaration(assignment)};
- else
- return {};
- }
- else
- return {};
- }
- );
-
- swap(m_emptyVarDecls, outerEmptyVarDecls);
- swap(m_lazyInitializedVarDecls, outerLazyInitializedVarDecls);
-}
-
-void VarDeclPropagator::operator()(VariableDeclaration& _varDecl)
-{
- if (_varDecl.value)
- visit(*_varDecl.value);
- else
- for (TypedName const& typedName: _varDecl.variables)
- m_emptyVarDecls[typedName.name] = typedName;
-}
-
-void VarDeclPropagator::operator()(Assignment& _assignment)
-{
- visit(*_assignment.value);
-
- if (allVarNamesUninitialized(_assignment.variableNames))
- for (Identifier const& ident: _assignment.variableNames)
- m_lazyInitializedVarDecls[ident.name] = m_emptyVarDecls[ident.name];
-
- for (Identifier& name: _assignment.variableNames)
- (*this)(name);
-}
-
-void VarDeclPropagator::operator()(Identifier& _ident)
-{
- m_emptyVarDecls.erase(_ident.name);
-}
-
-bool VarDeclPropagator::allVarNamesUninitialized(vector<Identifier> const& _variableNames) const
-{
- return all_of(
- begin(_variableNames),
- end(_variableNames),
- [&](Identifier const& _ident) -> bool { return m_emptyVarDecls.count(_ident.name); }
- );
-}
-
-bool VarDeclPropagator::isFullyLazyInitialized(vector<Identifier> const& _variableNames) const
-{
- return all_of(
- begin(_variableNames),
- end(_variableNames),
- [&](Identifier const& ident) -> bool { return m_lazyInitializedVarDecls.count(ident.name); }
- );
-}
-
-VariableDeclaration VarDeclPropagator::recreateVariableDeclaration(Assignment& _assignment)
-{
- TypedNameList variables;
-
- for (Identifier const& varName: _assignment.variableNames)
- {
- variables.emplace_back(move(m_lazyInitializedVarDecls.at(varName.name)));
- m_lazyInitializedVarDecls.erase(varName.name);
- }
-
- return VariableDeclaration{move(_assignment.location), move(variables), std::move(_assignment.value)};
-}
diff --git a/libyul/optimiser/VarDeclPropagator.h b/libyul/optimiser/VarDeclPropagator.h
deleted file mode 100644
index 1908c214..00000000
--- a/libyul/optimiser/VarDeclPropagator.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- 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 <libyul/AsmDataForward.h>
-#include <libyul/optimiser/ASTWalker.h>
-#include <libyul/Exceptions.h>
-#include <libyul/AsmDataForward.h>
-#include <vector>
-#include <set>
-#include <map>
-
-namespace yul
-{
-
-/**
- * Rewrites Assignment statements into VariableDeclaration when the assignment's LHS
- * variables had no value yet.
- *
- * It recursively walks through the AST and moves each declaration of variables to
- * the first assignment within the same block (if possible)..
- */
-class VarDeclPropagator: public ASTModifier
-{
-public:
- using ASTModifier::operator();
- void operator()(Block& _block) override;
- void operator()(VariableDeclaration& _varDecl) override;
- void operator()(Assignment& _assignment) override;
- void operator()(Identifier& _ident) override;
-
-private:
- bool allVarNamesUninitialized(std::vector<Identifier> const& _variableNames) const;
- bool isFullyLazyInitialized(std::vector<Identifier> const& _variableNames) const;
- VariableDeclaration recreateVariableDeclaration(Assignment& _assignment);
-
-private:
- /// Holds a list of variables from current Block that have no value assigned yet.
- std::map<YulString, TypedName> m_emptyVarDecls;
-
- /// Holds a list variables (and their TypedName) within the current block.
- std::map<YulString, TypedName> m_lazyInitializedVarDecls;
-};
-
-}
diff --git a/scripts/build.sh b/scripts/build.sh
index bddbb97a..6edd60bd 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -1,28 +1,26 @@
#!/usr/bin/env bash
+set -e
-if [ -z "$1" ]; then
+ROOTDIR="$(dirname "$0")/.."
+BUILDDIR="${ROOTDIR}/build"
+
+if [[ $# -eq 0 ]]; then
BUILD_TYPE=Release
else
BUILD_TYPE="$1"
fi
-cd $(dirname "$0")/.. &&
-
if [[ "$(git tag --points-at HEAD 2>/dev/null)" == v* ]]; then
- touch prerelease.txt
+ touch "${ROOTDIR}/prerelease.txt"
fi
-mkdir -p build &&
-cd build &&
-cmake .. -DCMAKE_BUILD_TYPE="$BUILD_TYPE" &&
-make -j2
+mkdir -p "${BUILDDIR}"
+cd "${BUILDDIR}"
-if [ $? -ne 0 ]; then
- echo "Failed to build"
- exit 1
-fi
+cmake .. -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "${@:2}"
+make -j2
-if [ -z $CI ]; then
- echo "Installing solc and soltest"
- install solc/solc /usr/local/bin && install test/soltest /usr/local/bin
+if [[ "${CI}" == "" ]]; then
+ echo "Installing ..."
+ sudo make install
fi
diff --git a/scripts/docker_deploy_manual.sh b/scripts/docker_deploy_manual.sh
index 0393d22d..30dd267c 100755
--- a/scripts/docker_deploy_manual.sh
+++ b/scripts/docker_deploy_manual.sh
@@ -37,6 +37,12 @@ tag_and_push()
rm -rf .git
docker build -t "$image":build -f scripts/Dockerfile .
tmp_container=$(docker create "$image":build sh)
+
+# Alpine image
+mkdir -p upload
+docker cp ${tmp_container}:/usr/bin/solc upload/solc-static-linux
+docker build -t "$image":build-alpine -f scripts/Dockerfile_alpine .
+
if [ "$branch" = "develop" ]
then
tag_and_push build nightly
diff --git a/scripts/release.bat b/scripts/release.bat
index b15b49b7..cfd0c838 100644
--- a/scripts/release.bat
+++ b/scripts/release.bat
@@ -29,13 +29,9 @@ REM ---------------------------------------------------------------------------
set CONFIGURATION=%1
set VERSION=%2
-IF "%VERSION%"=="2015" (
- set "DLLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll"
-) ELSE (
- set "DLLS=MSVC_DLLS_NOT_FOUND"
- FOR /d %%d IN ("C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\*"
- "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\*") DO set "DLLS=%%d\x86\Microsoft.VC141.CRT\msvc*.dll"
-)
+set "DLLS=MSVC_DLLS_NOT_FOUND"
+FOR /d %%d IN ("C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Redist\MSVC\*"
+ "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Redist\MSVC\*") DO set "DLLS=%%d\x86\Microsoft.VC141.CRT\msvc*.dll"
7z a solidity-windows.zip ^
.\build\solc\%CONFIGURATION%\solc.exe .\build\test\%CONFIGURATION%\soltest.exe ^
diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh
index 36a8ef7f..4ba0e644 100755
--- a/scripts/release_ppa.sh
+++ b/scripts/release_ppa.sh
@@ -28,6 +28,12 @@
## method = ftp
## incoming = ~ethereum/ethereum
## login = anonymous
+##
+## [ethereum-static]
+## fqdn = ppa.launchpad.net
+## method = ftp
+## incoming = ~ethereum/ethereum-static
+## login = anonymous
##
##############################################################################
@@ -41,34 +47,36 @@ else
branch=$1
fi
-if [ "$branch" = develop ]
-then
- pparepo=ethereum-dev
- ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/ethereum-dev/+files
-else
- pparepo=ethereum
- ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/ethereum/+files
-fi
-
keyid=70D110489D66E2F6
email=builds@ethereum.org
packagename=solc
-for distribution in trusty xenial bionic cosmic
+static_build_distribution=cosmic
+
+for distribution in xenial bionic cosmic STATIC
do
cd /tmp/
rm -rf $distribution
mkdir $distribution
cd $distribution
-# Dependency
-if [ $distribution = trusty -o $distribution = vivid ]
+if [ $distribution = STATIC ]
then
+ pparepo=ethereum-static
Z3DEPENDENCY=""
+ CMAKE_OPTIONS="-DSOLC_LINK_STATIC=On"
else
+ if [ "$branch" = develop ]
+ then
+ pparepo=ethereum-dev
+ else
+ pparepo=ethereum
+ fi
Z3DEPENDENCY="libz3-dev,
"
+ CMAKE_OPTIONS=""
fi
+ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/${pparepo}/+files
# Fetch source
git clone --depth 2 --recursive https://github.com/ethereum/solidity.git -b "$branch"
@@ -114,7 +122,7 @@ Priority: extra
Maintainer: Christian (Buildserver key) <builds@ethereum.org>
Build-Depends: ${Z3DEPENDENCY}debhelper (>= 9.0.0),
cmake,
- g++-4.8,
+ g++,
git,
libgmp-dev,
libboost-all-dev,
@@ -168,7 +176,7 @@ override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
override_dh_auto_configure:
- dh_auto_configure -- -DINSTALL_LLLC=Off
+ dh_auto_configure -- -DINSTALL_LLLC=Off -DTESTS=OFF ${CMAKE_OPTIONS}
EOF
cat <<EOF > debian/copyright
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
@@ -224,7 +232,12 @@ EMAIL="$email" dch -v 1:${debversion}-${versionsuffix} "git build of ${commithas
debuild -S -d -sa -us -uc
# prepare .changes file for Launchpad
-sed -i -e s/UNRELEASED/${distribution}/ -e s/urgency=medium/urgency=low/ ../*.changes
+if [ $distribution = STATIC ]
+then
+ sed -i -e s/UNRELEASED/${static_build_distribution}/ -e s/urgency=medium/urgency=low/ ../*.changes
+else
+ sed -i -e s/UNRELEASED/${distribution}/ -e s/urgency=medium/urgency=low/ ../*.changes
+fi
# check if ubuntu already has the source tarball
(
diff --git a/scripts/tests.sh b/scripts/tests.sh
index c284c05c..8a7a2fdc 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -130,21 +130,14 @@ function download_aleth()
elif [ -z $CI ]; then
ALETH_PATH="aleth"
else
- # Any time the hash is updated here, the "Running compiler tests" section should also be updated.
mkdir -p /tmp/test
- if grep -i trusty /etc/lsb-release >/dev/null 2>&1
- then
- # built from d661ac4fec0aeffbedcdc195f67f5ded0c798278 at 2018-06-20
- ALETH_BINARY=aleth_2018-06-20_trusty
- ALETH_HASH="54b8a5455e45b295e3a962f353ff8f1580ed106c"
- else
- # built from d661ac4fec0aeffbedcdc195f67f5ded0c798278 at 2018-06-20
- ALETH_BINARY=aleth_2018-06-20_artful
- ALETH_HASH="02e6c4b3d98299885e73f7db6c9e3fbe3d66d444"
- fi
- ALETH_PATH="/tmp/test/aleth"
- wget -q -O $ALETH_PATH https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ALETH_BINARY
- test "$(shasum $ALETH_PATH)" = "$ALETH_HASH $ALETH_PATH"
+ # Any time the hash is updated here, the "Running compiler tests" section should also be updated.
+ ALETH_HASH="8ce2f00539d2fd8b5f093d854c6999424f7494ff"
+ ALETH_VERSION=1.5.0-alpha.7
+ wget -q -O /tmp/test/aleth.tar.gz https://github.com/ethereum/aleth/releases/download/v${ALETH_VERSION}/aleth-${ALETH_VERSION}-linux-x86_64.tar.gz
+ test "$(shasum /tmp/test/aleth.tar.gz)" = "$ALETH_HASH /tmp/test/aleth.tar.gz"
+ tar -xf /tmp/test/aleth.tar.gz -C /tmp/test
+ ALETH_PATH="/tmp/test/bin/aleth"
sync
chmod +x $ALETH_PATH
sync # Otherwise we might get a "text file busy" error
@@ -156,7 +149,7 @@ function download_aleth()
# echos the PID
function run_aleth()
{
- $ALETH_PATH --test -d "${WORKDIR}" >/dev/null 2>&1 &
+ $ALETH_PATH --db memorydb --test -d "${WORKDIR}" >/dev/null 2>&1 &
echo $!
# Wait until the IPC endpoint is available.
while [ ! -S "${WORKDIR}/geth.ipc" ] ; do sleep 1; done
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index 38e778c6..bda1b78a 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -858,8 +858,7 @@ bool CommandLineInterface::processInput()
m_compiler.reset(new CompilerStack(fileReader));
- auto scannerFromSourceName = [&](string const& _sourceName) -> Scanner const& { return m_compiler->scanner(_sourceName); };
- SourceReferenceFormatter formatter(serr(false), scannerFromSourceName);
+ SourceReferenceFormatter formatter(serr(false));
try
{
@@ -1222,8 +1221,7 @@ bool CommandLineInterface::assemble(
for (auto const& sourceAndStack: assemblyStacks)
{
auto const& stack = sourceAndStack.second;
- auto scannerFromSourceName = [&](string const&) -> Scanner const& { return stack.scanner(); };
- SourceReferenceFormatter formatter(serr(false), scannerFromSourceName);
+ SourceReferenceFormatter formatter(serr(false));
for (auto const& error: stack.errors())
{
diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h
index 0b22ca29..ff294adc 100644
--- a/solc/CommandLineInterface.h
+++ b/solc/CommandLineInterface.h
@@ -41,8 +41,6 @@ enum class DocumentationType: uint8_t;
class CommandLineInterface
{
public:
- CommandLineInterface() {}
-
/// Parse command line arguments and return false if we should not continue
bool parseArguments(int _argc, char** _argv);
/// Parse the files and create source code objects
diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h
new file mode 100644
index 00000000..be076059
--- /dev/null
+++ b/test/InteractiveTests.h
@@ -0,0 +1,63 @@
+/*
+ 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/TestCase.h>
+#include <test/libsolidity/ASTJSONTest.h>
+#include <test/libsolidity/SyntaxTest.h>
+#include <test/libsolidity/SMTCheckerJSONTest.h>
+#include <test/libyul/YulOptimizerTest.h>
+#include <test/libyul/ObjectCompilerTest.h>
+
+#include <boost/filesystem.hpp>
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+/** Container for all information regarding a testsuite */
+struct Testsuite
+{
+ char const* title;
+ boost::filesystem::path const path;
+ boost::filesystem::path const subpath;
+ bool smt;
+ bool ipc;
+ TestCase::TestCaseCreator testCaseCreator;
+};
+
+
+/// Array of testsuits that can be run interactively as well as automatically
+Testsuite const g_interactiveTestsuites[] = {
+/*
+ Title Path Subpath SMT IPC Creator function */
+ {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create},
+ {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create},
+ {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create},
+ {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
+ {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SyntaxTest::create},
+ {"SMT Checker JSON", "libsolidity", "smtCheckerTestsJSON", true, false, &SMTCheckerTest::create}
+};
+
+}
+}
+}
+
diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp
index 0aae21a7..60848118 100644
--- a/test/RPCSession.cpp
+++ b/test/RPCSession.cpp
@@ -296,40 +296,7 @@ void RPCSession::test_mineBlocks(int _number)
{
u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString()));
BOOST_REQUIRE(rpcCall("test_mineBlocks", { to_string(_number) }, true) == true);
-
- // We auto-calibrate the time it takes to mine the transaction.
- // It would be better to go without polling, but that would probably need a change to the test client
-
- auto startTime = std::chrono::steady_clock::now();
- unsigned sleepTime = m_sleepTime;
- size_t tries = 0;
- for (; ; ++tries)
- {
- std::this_thread::sleep_for(chrono::milliseconds(sleepTime));
- auto endTime = std::chrono::steady_clock::now();
- unsigned timeSpent = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count();
- if (timeSpent > m_maxMiningTime)
- BOOST_FAIL("Error in test_mineBlocks: block mining timeout!");
- if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number)
- break;
- else
- sleepTime *= 2;
- }
- if (tries > 1)
- {
- m_successfulMineRuns = 0;
- m_sleepTime += 2;
- }
- else if (tries == 1)
- {
- m_successfulMineRuns++;
- if (m_successfulMineRuns > 5)
- {
- m_successfulMineRuns = 0;
- if (m_sleepTime > 2)
- m_sleepTime--;
- }
- }
+ BOOST_REQUIRE(fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) == startBlock + _number);
}
void RPCSession::test_modifyTimestamp(size_t _timestamp)
diff --git a/test/RPCSession.h b/test/RPCSession.h
index 6e1391b4..92f9da4a 100644
--- a/test/RPCSession.h
+++ b/test/RPCSession.h
@@ -136,9 +136,6 @@ private:
IPCSocket m_ipcSocket;
size_t m_rpcSequence = 1;
- unsigned m_maxMiningTime = 6000000; // 600 seconds
- unsigned m_sleepTime = 10; // 10 milliseconds
- unsigned m_successfulMineRuns = 0;
bool m_receiptHasStatusField = false;
std::vector<std::string> m_accounts;
diff --git a/test/TestCase.h b/test/TestCase.h
index 3c05ae4e..27320009 100644
--- a/test/TestCase.h
+++ b/test/TestCase.h
@@ -36,7 +36,7 @@ class TestCase
public:
using TestCaseCreator = std::unique_ptr<TestCase>(*)(std::string const&);
- virtual ~TestCase() {}
+ virtual ~TestCase() = default;
/// Runs the test case.
/// Outputs error messages to @arg _stream. Each line of output is prefixed with @arg _linePrefix.
diff --git a/test/boostTest.cpp b/test/boostTest.cpp
index 7cb0c143..d6e75cb9 100644
--- a/test/boostTest.cpp
+++ b/test/boostTest.cpp
@@ -35,15 +35,13 @@
#pragma GCC diagnostic pop
+#include <test/InteractiveTests.h>
#include <test/Options.h>
-#include <test/libsolidity/ASTJSONTest.h>
-#include <test/libsolidity/SyntaxTest.h>
-#include <test/libsolidity/SMTCheckerJSONTest.h>
-#include <test/libyul/YulOptimizerTest.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
+#include <string>
using namespace boost::unit_test;
using namespace dev::solidity::test;
@@ -128,40 +126,26 @@ 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(registerTests(
- master,
- dev::test::Options::get().testPath / "libsolidity",
- "syntaxTests",
- SyntaxTest::create
- ) > 0, "no syntax tests found");
- solAssert(registerTests(
- master,
- dev::test::Options::get().testPath / "libsolidity",
- "ASTJSON",
- ASTJSONTest::create
- ) > 0, "no JSON AST tests found");
- solAssert(registerTests(
- master,
- dev::test::Options::get().testPath / "libyul",
- "yulOptimizerTests",
- yul::test::YulOptimizerTest::create
- ) > 0, "no Yul Optimizer tests found");
- if (!dev::test::Options::get().disableSMT)
+
+ // Include the interactive tests in the automatic tests as well
+ for (auto const& ts: g_interactiveTestsuites)
{
- solAssert(registerTests(
- master,
- dev::test::Options::get().testPath / "libsolidity",
- "smtCheckerTests",
- SyntaxTest::create
- ) > 0, "no SMT checker tests found");
+ auto const& options = dev::test::Options::get();
+
+ if (ts.smt && options.disableSMT)
+ continue;
+
+ if (ts.ipc && options.disableIPC)
+ continue;
solAssert(registerTests(
master,
- dev::test::Options::get().testPath / "libsolidity",
- "smtCheckerTestsJSON",
- SMTCheckerTest::create
- ) > 0, "no SMT checker JSON tests found");
+ options.testPath / ts.path,
+ ts.subpath,
+ ts.testCaseCreator
+ ) > 0, std::string("no ") + ts.title + " tests found");
}
+
if (dev::test::Options::get().disableIPC)
{
for (auto suite: {
@@ -176,11 +160,13 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
"LLLEndToEndTest",
#endif
"GasMeterTests",
+ "GasCostTests",
"SolidityEndToEndTest",
"SolidityOptimizer"
})
removeTestSuite(suite);
}
+
if (dev::test::Options::get().disableSMT)
removeTestSuite("SMTChecker");
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index fdc9fbe3..9d2ffa5f 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -110,30 +110,42 @@ printTask "Testing unknown options..."
# General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr.
# An failure is expected.
-test_solc_file_input_failures() {
+test_solc_behaviour() {
local filename="${1}"
local solc_args="${2}"
- local stdout_expected="${3}"
- local stderr_expected="${4}"
+ local solc_stdin="${3}"
+ local stdout_expected="${4}"
+ local exit_code_expected="${5}"
+ local stderr_expected="${6}"
local stdout_path=`mktemp`
local stderr_path=`mktemp`
+ if [[ "$exit_code_expected" = "" ]]; then exit_code_expected="0"; fi
set +e
- "$SOLC" "${filename}" ${solc_args} 1>$stdout_path 2>$stderr_path
+ if [[ "$solc_stdin" = "" ]]; then
+ "$SOLC" "${filename}" ${solc_args} 1>$stdout_path 2>$stderr_path
+ else
+ "$SOLC" "${filename}" ${solc_args} <$solc_stdin 1>$stdout_path 2>$stderr_path
+ fi
exitCode=$?
set -e
- sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
- sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path"
+ if [[ "$solc_args" == *"--standard-json"* ]]; then
+ sed -i -e 's/{[^{]*Warning: This is a pre-release compiler version[^}]*},\{0,1\}//' "$stdout_path"
+ sed -i -e 's/"errors":\[\],\{0,1\}//' "$stdout_path"
+ else
+ sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path"
+ sed -i -e 's/ Consider adding "pragma .*$//' "$stderr_path"
+ fi
- if [[ $exitCode -eq 0 ]]; then
- printError "Incorrect exit code. Expected failure (non-zero) but got success (0)."
+ if [[ $exitCode -ne "$exit_code_expected" ]]; then
+ printError "Incorrect exit code. Expected $exit_code_expected but got $exitCode."
rm -f $stdout_path $stderr_path
exit 1
fi
if [[ "$(cat $stdout_path)" != "${stdout_expected}" ]]; then
- printError "Incorrect output on stderr received. Expected:"
+ printError "Incorrect output on stdout received. Expected:"
echo -e "${stdout_expected}"
printError "But got:"
@@ -156,22 +168,41 @@ test_solc_file_input_failures() {
}
printTask "Testing passing files that are not found..."
-test_solc_file_input_failures "file_not_found.sol" "" "" "\"file_not_found.sol\" is not found."
+test_solc_behaviour "file_not_found.sol" "" "" "" 1 "\"file_not_found.sol\" is not found."
printTask "Testing passing files that are not files..."
-test_solc_file_input_failures "." "" "" "\".\" is not a valid file."
+test_solc_behaviour "." "" "" "" 1 "\".\" is not a valid file."
printTask "Testing passing empty remappings..."
-test_solc_file_input_failures "${0}" "=/some/remapping/target" "" "Invalid remapping: \"=/some/remapping/target\"."
-test_solc_file_input_failures "${0}" "ctx:=/some/remapping/target" "" "Invalid remapping: \"ctx:=/some/remapping/target\"."
+test_solc_behaviour "${0}" "=/some/remapping/target" "" "" 1 "Invalid remapping: \"=/some/remapping/target\"."
+test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "Invalid remapping: \"ctx:=/some/remapping/target\"."
+
+printTask "Running standard JSON commandline tests..."
+(
+cd "$REPO_ROOT"/test/cmdlineTests/
+for file in *.json
+do
+ args="--standard-json"
+ stdin="$REPO_ROOT/test/cmdlineTests/$file"
+ stdout=$(cat $file.stdout 2>/dev/null || true)
+ exitCode=$(cat $file.exit 2>/dev/null || true)
+ err=$(cat $file.err 2>/dev/null || true)
+ printTask " - $file"
+ test_solc_behaviour "" "$args" "$stdin" "$stdout" "$exitCode" "$err"
+done
+)
-printTask "Testing passing location printing..."
+printTask "Running general commandline tests..."
(
-cd "$REPO_ROOT"/test/cmdlineErrorReports/
+cd "$REPO_ROOT"/test/cmdlineTests/
for file in *.sol
do
- ret=`cat $file.ref`
- test_solc_file_input_failures "$file" "" "" "$ret"
+ args=$(cat $file.args 2>/dev/null || true)
+ stdout=$(cat $file.stdout 2>/dev/null || true)
+ exitCode=$(cat $file.exit 2>/dev/null || true)
+ err=$(cat $file.err 2>/dev/null || true)
+ printTask " - $file"
+ test_solc_behaviour "$file" "$args" "" "$stdout" "$exitCode" "$err"
done
)
diff --git a/test/cmdlineTests/data_storage.sol b/test/cmdlineTests/data_storage.sol
new file mode 100644
index 00000000..cc602cc9
--- /dev/null
+++ b/test/cmdlineTests/data_storage.sol
@@ -0,0 +1,15 @@
+pragma solidity >=0.0;
+
+contract C {
+ function f() pure public {
+ require(false, "1234567890123456789012345678901");
+ require(false, "12345678901234567890123456789012");
+ require(false, "123456789012345678901234567890123");
+ require(false, "1234567890123456789012345678901234");
+ require(false, "12345678901234567890123456789012345");
+ require(false, "123456789012345678901234567890123456");
+ require(false, "123456789012345678901234567890121234567890123456789012345678901");
+ require(false, "1234567890123456789012345678901212345678901234567890123456789012");
+ require(false, "12345678901234567890123456789012123456789012345678901234567890123");
+ }
+}
diff --git a/test/cmdlineTests/data_storage.sol.args b/test/cmdlineTests/data_storage.sol.args
new file mode 100644
index 00000000..3684987e
--- /dev/null
+++ b/test/cmdlineTests/data_storage.sol.args
@@ -0,0 +1 @@
+--gas
diff --git a/test/cmdlineTests/data_storage.sol.stdout b/test/cmdlineTests/data_storage.sol.stdout
new file mode 100644
index 00000000..4a5250f7
--- /dev/null
+++ b/test/cmdlineTests/data_storage.sol.stdout
@@ -0,0 +1,7 @@
+
+======= data_storage.sol:C =======
+Gas estimation:
+construction:
+ 306 + 264400 = 264706
+external:
+ f(): 263
diff --git a/test/cmdlineTests/gas_test_dispatch.sol b/test/cmdlineTests/gas_test_dispatch.sol
new file mode 100644
index 00000000..a5ca9e7d
--- /dev/null
+++ b/test/cmdlineTests/gas_test_dispatch.sol
@@ -0,0 +1,43 @@
+pragma solidity >=0.0;
+
+contract Large {
+ uint public a;
+ uint[] public b;
+ function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; }
+ function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; }
+ function f4(uint x) public returns (uint) { b[uint8(msg.data[3])] = x; }
+ function f5(uint x) public returns (uint) { b[uint8(msg.data[4])] = x; }
+ function f6(uint x) public returns (uint) { b[uint8(msg.data[5])] = x; }
+ function f7(uint x) public returns (uint) { b[uint8(msg.data[6])] = x; }
+ function f8(uint x) public returns (uint) { b[uint8(msg.data[7])] = x; }
+ function f9(uint x) public returns (uint) { b[uint8(msg.data[8])] = x; }
+ function f0(uint x) public pure returns (uint) { require(x > 10); }
+ function g1(uint x) public payable returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function g2(uint x) public payable returns (uint) { b[uint8(msg.data[1])] = x; }
+ function g3(uint x) public payable returns (uint) { b[uint8(msg.data[2])] = x; }
+ function g4(uint x) public payable returns (uint) { b[uint8(msg.data[3])] = x; }
+ function g5(uint x) public payable returns (uint) { b[uint8(msg.data[4])] = x; }
+ function g6(uint x) public payable returns (uint) { b[uint8(msg.data[5])] = x; }
+ function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; }
+ function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; }
+ function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; }
+ function g0(uint x) public payable returns (uint) { require(x > 10); }
+}
+contract Medium {
+ uint public a;
+ uint[] public b;
+ function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; }
+ function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; }
+ function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; }
+ function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; }
+ function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; }
+ function g0(uint x) public payable returns (uint) { require(x > 10); }
+}
+contract Small {
+ uint public a;
+ uint[] public b;
+ function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function () external payable {}
+}
diff --git a/test/cmdlineTests/gas_test_dispatch.sol.args b/test/cmdlineTests/gas_test_dispatch.sol.args
new file mode 100644
index 00000000..3684987e
--- /dev/null
+++ b/test/cmdlineTests/gas_test_dispatch.sol.args
@@ -0,0 +1 @@
+--gas
diff --git a/test/cmdlineTests/gas_test_dispatch.sol.stdout b/test/cmdlineTests/gas_test_dispatch.sol.stdout
new file mode 100644
index 00000000..117affad
--- /dev/null
+++ b/test/cmdlineTests/gas_test_dispatch.sol.stdout
@@ -0,0 +1,53 @@
+
+======= gas_test_dispatch.sol:Large =======
+Gas estimation:
+construction:
+ 1034 + 998400 = 999434
+external:
+ a(): 456
+ b(uint256): 857
+ f0(uint256): 438
+ f1(uint256): 40781
+ f2(uint256): 20722
+ f3(uint256): 20810
+ f4(uint256): 20788
+ f5(uint256): 20766
+ f6(uint256): 20789
+ f7(uint256): 20701
+ f8(uint256): 20701
+ f9(uint256): 20723
+ g0(uint256): 324
+ g1(uint256): 40736
+ g2(uint256): 20699
+ g3(uint256): 20787
+ g4(uint256): 20765
+ g5(uint256): 20721
+ g6(uint256): 20744
+ g7(uint256): 20743
+ g8(uint256): 20721
+ g9(uint256): 20678
+
+======= gas_test_dispatch.sol:Medium =======
+Gas estimation:
+construction:
+ 411 + 376600 = 377011
+external:
+ a(): 433
+ b(uint256): 857
+ f1(uint256): 40692
+ f2(uint256): 20722
+ f3(uint256): 20766
+ g0(uint256): 324
+ g7(uint256): 20721
+ g8(uint256): 20699
+ g9(uint256): 20655
+
+======= gas_test_dispatch.sol:Small =======
+Gas estimation:
+construction:
+ 153 + 107800 = 107953
+external:
+ fallback: 123
+ a(): 388
+ b(uint256): 813
+ f1(uint256): 40692
diff --git a/test/cmdlineTests/gas_test_dispatch_optimize.sol b/test/cmdlineTests/gas_test_dispatch_optimize.sol
new file mode 100644
index 00000000..a5ca9e7d
--- /dev/null
+++ b/test/cmdlineTests/gas_test_dispatch_optimize.sol
@@ -0,0 +1,43 @@
+pragma solidity >=0.0;
+
+contract Large {
+ uint public a;
+ uint[] public b;
+ function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; }
+ function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; }
+ function f4(uint x) public returns (uint) { b[uint8(msg.data[3])] = x; }
+ function f5(uint x) public returns (uint) { b[uint8(msg.data[4])] = x; }
+ function f6(uint x) public returns (uint) { b[uint8(msg.data[5])] = x; }
+ function f7(uint x) public returns (uint) { b[uint8(msg.data[6])] = x; }
+ function f8(uint x) public returns (uint) { b[uint8(msg.data[7])] = x; }
+ function f9(uint x) public returns (uint) { b[uint8(msg.data[8])] = x; }
+ function f0(uint x) public pure returns (uint) { require(x > 10); }
+ function g1(uint x) public payable returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function g2(uint x) public payable returns (uint) { b[uint8(msg.data[1])] = x; }
+ function g3(uint x) public payable returns (uint) { b[uint8(msg.data[2])] = x; }
+ function g4(uint x) public payable returns (uint) { b[uint8(msg.data[3])] = x; }
+ function g5(uint x) public payable returns (uint) { b[uint8(msg.data[4])] = x; }
+ function g6(uint x) public payable returns (uint) { b[uint8(msg.data[5])] = x; }
+ function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; }
+ function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; }
+ function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; }
+ function g0(uint x) public payable returns (uint) { require(x > 10); }
+}
+contract Medium {
+ uint public a;
+ uint[] public b;
+ function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function f2(uint x) public returns (uint) { b[uint8(msg.data[1])] = x; }
+ function f3(uint x) public returns (uint) { b[uint8(msg.data[2])] = x; }
+ function g7(uint x) public payable returns (uint) { b[uint8(msg.data[6])] = x; }
+ function g8(uint x) public payable returns (uint) { b[uint8(msg.data[7])] = x; }
+ function g9(uint x) public payable returns (uint) { b[uint8(msg.data[8])] = x; }
+ function g0(uint x) public payable returns (uint) { require(x > 10); }
+}
+contract Small {
+ uint public a;
+ uint[] public b;
+ function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
+ function () external payable {}
+}
diff --git a/test/cmdlineTests/gas_test_dispatch_optimize.sol.args b/test/cmdlineTests/gas_test_dispatch_optimize.sol.args
new file mode 100644
index 00000000..814e0591
--- /dev/null
+++ b/test/cmdlineTests/gas_test_dispatch_optimize.sol.args
@@ -0,0 +1 @@
+--optimize --optimize-runs 2 --gas
diff --git a/test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout b/test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout
new file mode 100644
index 00000000..76fa086e
--- /dev/null
+++ b/test/cmdlineTests/gas_test_dispatch_optimize.sol.stdout
@@ -0,0 +1,53 @@
+
+======= gas_test_dispatch_optimize.sol:Large =======
+Gas estimation:
+construction:
+ 300 + 262000 = 262300
+external:
+ a(): 463
+ b(uint256): 1173
+ f0(uint256): 399
+ f1(uint256): 41164
+ f2(uint256): 21224
+ f3(uint256): 21312
+ f4(uint256): 21290
+ f5(uint256): 21268
+ f6(uint256): 21180
+ f7(uint256): 20960
+ f8(uint256): 21092
+ f9(uint256): 21114
+ g0(uint256): 639
+ g1(uint256): 40876
+ g2(uint256): 20958
+ g3(uint256): 21046
+ g4(uint256): 21024
+ g5(uint256): 21112
+ g6(uint256): 20892
+ g7(uint256): 21002
+ g8(uint256): 20980
+ g9(uint256): 20826
+
+======= gas_test_dispatch_optimize.sol:Medium =======
+Gas estimation:
+construction:
+ 190 + 143000 = 143190
+external:
+ a(): 463
+ b(uint256): 931
+ f1(uint256): 40944
+ f2(uint256): 20982
+ f3(uint256): 21026
+ g0(uint256): 397
+ g7(uint256): 20892
+ g8(uint256): 20870
+ g9(uint256): 20826
+
+======= gas_test_dispatch_optimize.sol:Small =======
+Gas estimation:
+construction:
+ 117 + 67400 = 67517
+external:
+ fallback: 183
+ a(): 441
+ b(uint256): 821
+ f1(uint256): 40878
diff --git a/test/cmdlineTests/standard.json b/test/cmdlineTests/standard.json
new file mode 100644
index 00000000..826253b8
--- /dev/null
+++ b/test/cmdlineTests/standard.json
@@ -0,0 +1,10 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard.json.exit b/test/cmdlineTests/standard.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard.json.stdout b/test/cmdlineTests/standard.json.stdout
new file mode 100644
index 00000000..59b90c8c
--- /dev/null
+++ b/test/cmdlineTests/standard.json.stdout
@@ -0,0 +1 @@
+{"sources":{"A":{"id":0}}}
diff --git a/test/cmdlineTests/standard_binaries_requested.json b/test/cmdlineTests/standard_binaries_requested.json
new file mode 100644
index 00000000..65f19543
--- /dev/null
+++ b/test/cmdlineTests/standard_binaries_requested.json
@@ -0,0 +1,17 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "outputSelection":
+ {
+ "*": { "*": ["evm.gasEstimates"] }
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_binaries_requested.json.stdout b/test/cmdlineTests/standard_binaries_requested.json.stdout
new file mode 100644
index 00000000..2baef22a
--- /dev/null
+++ b/test/cmdlineTests/standard_binaries_requested.json.stdout
@@ -0,0 +1 @@
+{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"25600","executionCost":"75","totalCost":"25675"},"external":{"f()":"127"}}}}}},"sources":{"A":{"id":0}}} \ No newline at end of file
diff --git a/test/cmdlineTests/standard_methodIdentifiersRequested.json b/test/cmdlineTests/standard_methodIdentifiersRequested.json
new file mode 100644
index 00000000..79a3c75d
--- /dev/null
+++ b/test/cmdlineTests/standard_methodIdentifiersRequested.json
@@ -0,0 +1,17 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "outputSelection":
+ {
+ "*": { "*": ["evm.methodIdentifiers"] }
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout b/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout
new file mode 100644
index 00000000..7e3f139f
--- /dev/null
+++ b/test/cmdlineTests/standard_methodIdentifiersRequested.json.stdout
@@ -0,0 +1 @@
+{"contracts":{"A":{"C":{"evm":{"methodIdentifiers":{"f()":"26121ff0"}}}}},"sources":{"A":{"id":0}}}
diff --git a/test/cmdlineTests/standard_only_ast_requested.json b/test/cmdlineTests/standard_only_ast_requested.json
new file mode 100644
index 00000000..7abd6da5
--- /dev/null
+++ b/test/cmdlineTests/standard_only_ast_requested.json
@@ -0,0 +1,17 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "outputSelection":
+ {
+ "*": { "": ["ast"] }
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_only_ast_requested.json.stdout b/test/cmdlineTests/standard_only_ast_requested.json.stdout
new file mode 100644
index 00000000..b884ab7d
--- /dev/null
+++ b/test/cmdlineTests/standard_only_ast_requested.json.stdout
@@ -0,0 +1 @@
+{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"0:22:0"},{"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"61:2:0","statements":[]},"documentation":null,"id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"46:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"61:0:0"},"scope":6,"src":"36:27:0","stateMutability":"pure","superFunction":null,"visibility":"public"}],"scope":7,"src":"23:42:0"}],"src":"0:65:0"},"id":0}}}
diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json
new file mode 100644
index 00000000..51dbce41
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json
@@ -0,0 +1,14 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "auxiliaryInput":
+ {
+ "key1": "test"
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout
new file mode 100644
index 00000000..077ac47e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_auxiliary_input.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_key_metadata.json b/test/cmdlineTests/standard_wrong_key_metadata.json
new file mode 100644
index 00000000..490e489a
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_metadata.json
@@ -0,0 +1,22 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "optimizer": {
+ "enabled": true,
+ "runs": 200
+ },
+ "evmVersion": "byzantium",
+ "metadata": {
+ "key1": "test",
+ "useLiteralContent": true
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_key_metadata.json.exit b/test/cmdlineTests/standard_wrong_key_metadata.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_metadata.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_key_metadata.json.stdout b/test/cmdlineTests/standard_wrong_key_metadata.json.stdout
new file mode 100644
index 00000000..077ac47e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_metadata.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_key_optimizer.json b/test/cmdlineTests/standard_wrong_key_optimizer.json
new file mode 100644
index 00000000..c28c3a92
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_optimizer.json
@@ -0,0 +1,22 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "optimizer": {
+ "key1": "test",
+ "enabled": true,
+ "runs": 200
+ },
+ "evmVersion": "byzantium",
+ "metadata": {
+ "useLiteralContent": true
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_key_optimizer.json.exit b/test/cmdlineTests/standard_wrong_key_optimizer.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_optimizer.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_key_optimizer.json.stdout b/test/cmdlineTests/standard_wrong_key_optimizer.json.stdout
new file mode 100644
index 00000000..077ac47e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_optimizer.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_key_root.json b/test/cmdlineTests/standard_wrong_key_root.json
new file mode 100644
index 00000000..4689c50c
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_root.json
@@ -0,0 +1,11 @@
+{
+ "language": "Solidity",
+ "key1": "test",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_key_root.json.exit b/test/cmdlineTests/standard_wrong_key_root.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_root.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_key_root.json.stdout b/test/cmdlineTests/standard_wrong_key_root.json.stdout
new file mode 100644
index 00000000..077ac47e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_root.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_key_settings.json b/test/cmdlineTests/standard_wrong_key_settings.json
new file mode 100644
index 00000000..d7809b1c
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_settings.json
@@ -0,0 +1,22 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "optimizer": {
+ "enabled": true,
+ "runs": 200
+ },
+ "evmVersion": "byzantium",
+ "metadata": {
+ "useLiteralContent": true
+ },
+ "key1": "test"
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_key_settings.json.exit b/test/cmdlineTests/standard_wrong_key_settings.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_settings.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_key_settings.json.stdout b/test/cmdlineTests/standard_wrong_key_settings.json.stdout
new file mode 100644
index 00000000..077ac47e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_settings.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_key_source.json b/test/cmdlineTests/standard_wrong_key_source.json
new file mode 100644
index 00000000..d8a8aa16
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_source.json
@@ -0,0 +1,11 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "key1": "test",
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_key_source.json.exit b/test/cmdlineTests/standard_wrong_key_source.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_source.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_key_source.json.stdout b/test/cmdlineTests/standard_wrong_key_source.json.stdout
new file mode 100644
index 00000000..077ac47e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_key_source.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Unknown key \"key1\"","message":"Unknown key \"key1\"","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json
new file mode 100644
index 00000000..8d2c7593
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json
@@ -0,0 +1,11 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "auxiliaryInput": [1, 2, 3]
+}
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout
new file mode 100644
index 00000000..046cb6d9
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input.json.stdout
@@ -0,0 +1,2 @@
+{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput\" must be an object","message":"\"auxiliaryInput\" must be an object","severity":"error","type":"JSONError"}]}
+
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json
new file mode 100644
index 00000000..9175050f
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json
@@ -0,0 +1,20 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": {
+ "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
+ "": [ "legacyAST" ]
+ }
+ }
+ },
+ "auxiliaryInput":
+ {
+ "smtlib2responses": "not an object"
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout
new file mode 100644
index 00000000..3efaea20
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"auxiliaryInput.smtlib2responses\" must be an object.","message":"\"auxiliaryInput.smtlib2responses\" must be an object.","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json
new file mode 100644
index 00000000..aa7d451b
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json
@@ -0,0 +1,23 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": {
+ "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
+ "": [ "legacyAST" ]
+ }
+ }
+ },
+ "auxiliaryInput":
+ {
+ "smtlib2responses":
+ {
+ "abc": ["not a string"]
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout
new file mode 100644
index 00000000..a05176be
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"smtlib2Responses.abc\" must be a string.","message":"\"smtlib2Responses.abc\" must be a string.","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_metadata.json b/test/cmdlineTests/standard_wrong_type_metadata.json
new file mode 100644
index 00000000..d4dd06a1
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_metadata.json
@@ -0,0 +1,19 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "optimizer": {
+ "enabled": true,
+ "runs": 200
+ },
+ "evmVersion": "byzantium",
+ "metadata": ["meta1", "meta2", "meta3"]
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_metadata.json.exit b/test/cmdlineTests/standard_wrong_type_metadata.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_metadata.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_metadata.json.stdout b/test/cmdlineTests/standard_wrong_type_metadata.json.stdout
new file mode 100644
index 00000000..7b997cec
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_metadata.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.metadata\" must be an object","message":"\"settings.metadata\" must be an object","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_optimizer.json b/test/cmdlineTests/standard_wrong_type_optimizer.json
new file mode 100644
index 00000000..b42ca550
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_optimizer.json
@@ -0,0 +1,18 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ {
+ "optimizer": 1,
+ "evmVersion": "byzantium",
+ "metadata": {
+ "useLiteralContent": true
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_optimizer.json.exit b/test/cmdlineTests/standard_wrong_type_optimizer.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_optimizer.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_optimizer.json.stdout b/test/cmdlineTests/standard_wrong_type_optimizer.json.stdout
new file mode 100644
index 00000000..d43b6470
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_optimizer.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer\" must be an object","message":"\"settings.optimizer\" must be an object","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection.json b/test/cmdlineTests/standard_wrong_type_output_selection.json
new file mode 100644
index 00000000..a7b615d1
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection.json
@@ -0,0 +1,11 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": "not an object"
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection.json.stdout
new file mode 100644
index 00000000..39e74882
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection\" must be an object","message":"\"settings.outputSelection\" must be an object","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json
new file mode 100644
index 00000000..9840a97e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json
@@ -0,0 +1,16 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": {
+ "A": "it's a contract, but not an array!",
+ "": [ "legacyAST" ]
+ }
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout
new file mode 100644
index 00000000..a4ba320e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file.json b/test/cmdlineTests/standard_wrong_type_output_selection_file.json
new file mode 100644
index 00000000..7ab12ba8
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_file.json
@@ -0,0 +1,13 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": "awesome file!"
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_file.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout
new file mode 100644
index 00000000..8874e636
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_file.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA\" must be an object","message":"\"settings.outputSelection.fileA\" must be an object","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output.json b/test/cmdlineTests/standard_wrong_type_output_selection_output.json
new file mode 100644
index 00000000..3e5cd661
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_output.json
@@ -0,0 +1,16 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": {
+ "A": [ 1, 2, 3 ,4],
+ "": [ "legacyAST" ]
+ }
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit b/test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_output.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout b/test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout
new file mode 100644
index 00000000..a4ba320e
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_output_selection_output.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.outputSelection.fileA.A\" must be a string array","message":"\"settings.outputSelection.fileA.A\" must be a string array","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_remappings.json b/test/cmdlineTests/standard_wrong_type_remappings.json
new file mode 100644
index 00000000..1436e014
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_remappings.json
@@ -0,0 +1,17 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": {
+ "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
+ "": [ "legacyAST" ]
+ }
+ },
+ "remappings": "not an object"
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_remappings.json.exit b/test/cmdlineTests/standard_wrong_type_remappings.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_remappings.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_remappings.json.stdout b/test/cmdlineTests/standard_wrong_type_remappings.json.stdout
new file mode 100644
index 00000000..b5e4ea5c
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_remappings.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings.","message":"\"settings.remappings\" must be an array of strings.","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry.json b/test/cmdlineTests/standard_wrong_type_remappings_entry.json
new file mode 100644
index 00000000..c96611f3
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_remappings_entry.json
@@ -0,0 +1,17 @@
+{
+ "language": "Solidity",
+ "sources": {
+ "fileA": {
+ "content": "contract A { }"
+ }
+ },
+ "settings": {
+ "outputSelection": {
+ "fileA": {
+ "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ],
+ "": [ "legacyAST" ]
+ }
+ },
+ "remappings": [1, 2 ,3 ,4]
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit b/test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_remappings_entry.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout b/test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout
new file mode 100644
index 00000000..0fc71ded
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_remappings_entry.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings.remappings\" must be an array of strings","message":"\"settings.remappings\" must be an array of strings","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_root.json b/test/cmdlineTests/standard_wrong_type_root.json
new file mode 100644
index 00000000..4763607a
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_root.json
@@ -0,0 +1 @@
+["abc"]
diff --git a/test/cmdlineTests/standard_wrong_type_root.json.exit b/test/cmdlineTests/standard_wrong_type_root.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_root.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_root.json.stdout b/test/cmdlineTests/standard_wrong_type_root.json.stdout
new file mode 100644
index 00000000..15c12e77
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_root.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"Input is not a JSON object.","message":"Input is not a JSON object.","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_settings.json b/test/cmdlineTests/standard_wrong_type_settings.json
new file mode 100644
index 00000000..7cdb0881
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_settings.json
@@ -0,0 +1,23 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ },
+ "settings":
+ [
+ {
+ "optimizer": {
+ "enabled": true,
+ "runs": 200
+ },
+ "evmVersion": "byzantium",
+ "metadata": {
+ "useLiteralContent": true
+ }
+ }
+ ]
+}
diff --git a/test/cmdlineTests/standard_wrong_type_settings.json.exit b/test/cmdlineTests/standard_wrong_type_settings.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_settings.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_settings.json.stdout b/test/cmdlineTests/standard_wrong_type_settings.json.stdout
new file mode 100644
index 00000000..c78c6086
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_settings.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"settings\" must be an object","message":"\"settings\" must be an object","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_source.json b/test/cmdlineTests/standard_wrong_type_source.json
new file mode 100644
index 00000000..d58504fe
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_source.json
@@ -0,0 +1,12 @@
+{
+ "language": "Solidity",
+ "sources":
+ {
+ "A": "not an object :o",
+ "B": [1, 2, 3],
+ "C":
+ {
+ "content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
+ }
+ }
+}
diff --git a/test/cmdlineTests/standard_wrong_type_source.json.exit b/test/cmdlineTests/standard_wrong_type_source.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_source.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_source.json.stdout b/test/cmdlineTests/standard_wrong_type_source.json.stdout
new file mode 100644
index 00000000..98fe32fd
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_source.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"sources.A\" must be an object","message":"\"sources.A\" must be an object","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineTests/standard_wrong_type_sources.json b/test/cmdlineTests/standard_wrong_type_sources.json
new file mode 100644
index 00000000..76e1ae7d
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_sources.json
@@ -0,0 +1,4 @@
+{
+ "language": "Solidity",
+ "sources": ["source1", "source2", "source3"]
+}
diff --git a/test/cmdlineTests/standard_wrong_type_sources.json.exit b/test/cmdlineTests/standard_wrong_type_sources.json.exit
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_sources.json.exit
@@ -0,0 +1 @@
+0
diff --git a/test/cmdlineTests/standard_wrong_type_sources.json.stdout b/test/cmdlineTests/standard_wrong_type_sources.json.stdout
new file mode 100644
index 00000000..ac6c613f
--- /dev/null
+++ b/test/cmdlineTests/standard_wrong_type_sources.json.stdout
@@ -0,0 +1 @@
+{"errors":[{"component":"general","formattedMessage":"\"sources\" is not a JSON object.","message":"\"sources\" is not a JSON object.","severity":"error","type":"JSONError"}]}
diff --git a/test/cmdlineErrorReports/too_long_line.sol b/test/cmdlineTests/too_long_line.sol
index 7df1057a..7df1057a 100644
--- a/test/cmdlineErrorReports/too_long_line.sol
+++ b/test/cmdlineTests/too_long_line.sol
diff --git a/test/cmdlineErrorReports/too_long_line.sol.ref b/test/cmdlineTests/too_long_line.sol.err
index 55cd1935..55cd1935 100644
--- a/test/cmdlineErrorReports/too_long_line.sol.ref
+++ b/test/cmdlineTests/too_long_line.sol.err
diff --git a/test/cmdlineTests/too_long_line.sol.exit b/test/cmdlineTests/too_long_line.sol.exit
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/test/cmdlineTests/too_long_line.sol.exit
@@ -0,0 +1 @@
+1
diff --git a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol b/test/cmdlineTests/too_long_line_both_sides_short.sol
index 062f0292..062f0292 100644
--- a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol
+++ b/test/cmdlineTests/too_long_line_both_sides_short.sol
diff --git a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref b/test/cmdlineTests/too_long_line_both_sides_short.sol.err
index 9a5ebfba..9a5ebfba 100644
--- a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref
+++ b/test/cmdlineTests/too_long_line_both_sides_short.sol.err
diff --git a/test/cmdlineTests/too_long_line_both_sides_short.sol.exit b/test/cmdlineTests/too_long_line_both_sides_short.sol.exit
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/test/cmdlineTests/too_long_line_both_sides_short.sol.exit
@@ -0,0 +1 @@
+1
diff --git a/test/cmdlineErrorReports/too_long_line_edge_in.sol b/test/cmdlineTests/too_long_line_edge_in.sol
index 6f181c83..6f181c83 100644
--- a/test/cmdlineErrorReports/too_long_line_edge_in.sol
+++ b/test/cmdlineTests/too_long_line_edge_in.sol
diff --git a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref b/test/cmdlineTests/too_long_line_edge_in.sol.err
index ad3b7805..ad3b7805 100644
--- a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref
+++ b/test/cmdlineTests/too_long_line_edge_in.sol.err
diff --git a/test/cmdlineTests/too_long_line_edge_in.sol.exit b/test/cmdlineTests/too_long_line_edge_in.sol.exit
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/test/cmdlineTests/too_long_line_edge_in.sol.exit
@@ -0,0 +1 @@
+1
diff --git a/test/cmdlineErrorReports/too_long_line_edge_out.sol b/test/cmdlineTests/too_long_line_edge_out.sol
index 29d3cee6..29d3cee6 100644
--- a/test/cmdlineErrorReports/too_long_line_edge_out.sol
+++ b/test/cmdlineTests/too_long_line_edge_out.sol
diff --git a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref b/test/cmdlineTests/too_long_line_edge_out.sol.err
index d8495c11..d8495c11 100644
--- a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref
+++ b/test/cmdlineTests/too_long_line_edge_out.sol.err
diff --git a/test/cmdlineTests/too_long_line_edge_out.sol.exit b/test/cmdlineTests/too_long_line_edge_out.sol.exit
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/test/cmdlineTests/too_long_line_edge_out.sol.exit
@@ -0,0 +1 @@
+1
diff --git a/test/cmdlineErrorReports/too_long_line_left_short.sol b/test/cmdlineTests/too_long_line_left_short.sol
index 2accfcce..2accfcce 100644
--- a/test/cmdlineErrorReports/too_long_line_left_short.sol
+++ b/test/cmdlineTests/too_long_line_left_short.sol
diff --git a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref b/test/cmdlineTests/too_long_line_left_short.sol.err
index 00b6be5c..00b6be5c 100644
--- a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref
+++ b/test/cmdlineTests/too_long_line_left_short.sol.err
diff --git a/test/cmdlineTests/too_long_line_left_short.sol.exit b/test/cmdlineTests/too_long_line_left_short.sol.exit
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/test/cmdlineTests/too_long_line_left_short.sol.exit
@@ -0,0 +1 @@
+1
diff --git a/test/cmdlineErrorReports/too_long_line_right_short.sol b/test/cmdlineTests/too_long_line_right_short.sol
index 936b3961..936b3961 100644
--- a/test/cmdlineErrorReports/too_long_line_right_short.sol
+++ b/test/cmdlineTests/too_long_line_right_short.sol
diff --git a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref b/test/cmdlineTests/too_long_line_right_short.sol.err
index 88072d95..88072d95 100644
--- a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref
+++ b/test/cmdlineTests/too_long_line_right_short.sol.err
diff --git a/test/cmdlineTests/too_long_line_right_short.sol.exit b/test/cmdlineTests/too_long_line_right_short.sol.exit
new file mode 100644
index 00000000..d00491fd
--- /dev/null
+++ b/test/cmdlineTests/too_long_line_right_short.sol.exit
@@ -0,0 +1 @@
+1
diff --git a/test/externalTests.sh b/test/externalTests.sh
index 93581925..16f9e55a 100755
--- a/test/externalTests.sh
+++ b/test/externalTests.sh
@@ -79,6 +79,7 @@ function test_truffle
fi
# Change "compileStandard" to "compile"
sed -i s/solc.compileStandard/solc.compile/ "node_modules/truffle/build/cli.bundled.js"
+ npx truffle compile
npm run test
)
rm -rf "$DIR"
@@ -89,3 +90,5 @@ test_truffle Zeppelin https://github.com/axic/openzeppelin-solidity.git solidity
# Disabled temporarily as it needs to be updated to latest Truffle first.
#test_truffle Gnosis https://github.com/axic/pm-contracts.git solidity-050
+
+test_truffle GnosisSafe https://github.com/gnosis/safe-contracts.git development
diff --git a/test/libdevcore/CommonData.cpp b/test/libdevcore/CommonData.cpp
new file mode 100644
index 00000000..8da937de
--- /dev/null
+++ b/test/libdevcore/CommonData.cpp
@@ -0,0 +1,87 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Unit tests for the StringUtils routines.
+ */
+
+
+#include <libdevcore/CommonData.h>
+#include <libdevcore/FixedHash.h>
+#include <libsolidity/ast/Types.h> // for IntegerType
+
+#include <test/Options.h>
+
+using namespace std;
+using namespace dev::solidity;
+
+namespace dev
+{
+namespace test
+{
+
+BOOST_AUTO_TEST_SUITE(CommonData)
+
+BOOST_AUTO_TEST_CASE(test_to_hex)
+{
+ BOOST_CHECK_EQUAL(toHex(fromHex("FF"), HexPrefix::DontAdd, HexCase::Lower), "ff");
+}
+
+BOOST_AUTO_TEST_CASE(test_format_number)
+{
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x8000000)), "0x08000000");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x80000000)), "0x80000000");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x800000000)), "0x0800000000");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x8000000000)), "0x8000000000");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x80000000000)), "0x080000000000");
+
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x7ffffff)), "0x07ffffff");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x7fffffff)), "0x7fffffff");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x7ffffffff)), "0x07ffffffff");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x7fffffffff)), "0x7fffffffff");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x7ffffffffff)), "0x07ffffffffff");
+
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x88000000)), "0x88000000");
+ BOOST_CHECK_EQUAL(formatNumber(u256(0x8888888888000000)), "0x8888888888000000");
+
+ u256 b = 0;
+ for (int i = 0; i < 32; i++)
+ {
+ b <<= 8;
+ b |= 0x55;
+ }
+ u256 c = u256(FixedHash<32>(
+ fromHex("0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789")
+ ));
+ u256 d = u256(0xAAAAaaaaAAAAaaaa) << 192 |
+ u256(0xFFFFffffFFFFffff) << 128 |
+ u256(0xFFFFffffFFFFffff) << 64 |
+ u256(0xFFFFffffFFFFffff);
+ BOOST_CHECK_EQUAL(formatNumber(b), "0x5555555555555555555555555555555555555555555555555555555555555555");
+ BOOST_CHECK_EQUAL(formatNumber(c), "0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789");
+ BOOST_CHECK_EQUAL(formatNumber(d), "0xaaaaaaaaaaaaaaaaffffffffffffffffffffffffffffffffffffffffffffffff");
+
+ BOOST_CHECK_EQUAL(formatNumber(IntegerType(256).minValue()), "0");
+ BOOST_CHECK_EQUAL(
+ formatNumber(IntegerType(256).maxValue()),
+ "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+ );
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
diff --git a/test/libdevcore/StringUtils.cpp b/test/libdevcore/StringUtils.cpp
index 76c11b82..0baeb964 100644
--- a/test/libdevcore/StringUtils.cpp
+++ b/test/libdevcore/StringUtils.cpp
@@ -18,8 +18,12 @@
* Unit tests for the StringUtils routines.
*/
+#include <libdevcore/CommonData.h>
+#include <libdevcore/FixedHash.h>
#include <libdevcore/StringUtils.h>
+#include <libsolidity/ast/Types.h> // for IntegerType
+
#include <test/Options.h>
using namespace std;
@@ -100,6 +104,59 @@ BOOST_AUTO_TEST_CASE(test_human_readable_join)
BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b", "c"}), "; ", " or "), "a; b or c");
}
+BOOST_AUTO_TEST_CASE(test_format_number_readable)
+{
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8000000)), "0x08 * 2**24");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x80000000)), "0x80 * 2**24");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x800000000)), "0x08 * 2**32");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8000000000)), "0x80 * 2**32");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x80000000000)), "0x08 * 2**40");
+
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffff)), "0x08 * 2**24 - 1");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7fffffff)), "0x80 * 2**24 - 1");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffffff)), "0x08 * 2**32 - 1");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7fffffffff)), "0x80 * 2**32 - 1");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x7ffffffffff)), "0x08 * 2**40 - 1");
+
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x88000000)), "0x88 * 2**24");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x8888888888000000)), "0x8888888888 * 2**24");
+
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0x100000000)), "2**32");
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(0xFFFFffff)), "2**32 - 1");
+
+ u160 a = 0;
+ for (int i = 0; i < 20; i++)
+ {
+ a <<= 8;
+ a |= 0x55;
+ }
+ u256 b = 0;
+ for (int i = 0; i < 32; i++)
+ {
+ b <<= 8;
+ b |= 0x55;
+ }
+ u256 c = (u256)FixedHash<32>(
+ fromHex("0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789")
+ );
+ u256 d = u256(0xAAAAaaaaAAAAaaaa) << 192 |
+ u256(0xFFFFffffFFFFffff) << 128 |
+ u256(0xFFFFffffFFFFffff) << 64 |
+ u256(0xFFFFffffFFFFffff);
+ BOOST_CHECK_EQUAL(formatNumberReadable(a, true), "0x5555...{+32 more}...5555");
+ BOOST_CHECK_EQUAL(formatNumberReadable(b, true), "0x5555...{+56 more}...5555");
+ BOOST_CHECK_EQUAL(formatNumberReadable(c, true), "0xABCD...{+56 more}...6789");
+ BOOST_CHECK_EQUAL(formatNumberReadable(d, true), "0xAAAAaaaaAAAAaaab * 2**192 - 1");
+
+ //for codegen/ExpressionCompiler
+ BOOST_CHECK_EQUAL(formatNumberReadable(u256(-1)), "2**256 - 1");
+
+ // for formal/SMTChecker
+ BOOST_CHECK_EQUAL(
+ formatNumberReadable(solidity::IntegerType(256).minValue()), "0");
+ BOOST_CHECK_EQUAL(
+ formatNumberReadable(solidity::IntegerType(256).maxValue()), "2**256 - 1");
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp
index bece2be4..4e9040b9 100644
--- a/test/libevmasm/Assembler.cpp
+++ b/test/libevmasm/Assembler.cpp
@@ -56,11 +56,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
{
Assembly _assembly;
auto root_asm = make_shared<CharStream>("", "root.asm");
- _assembly.setSourceLocation(SourceLocation(1, 3, root_asm));
+ _assembly.setSourceLocation({1, 3, root_asm});
Assembly _subAsm;
auto sub_asm = make_shared<CharStream>("", "sub.asm");
- _subAsm.setSourceLocation(SourceLocation(6, 8, sub_asm));
+ _subAsm.setSourceLocation({6, 8, sub_asm});
_subAsm.append(Instruction::INVALID);
shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm);
@@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
_assembly.assemblyString(),
" /* \"root.asm\":1:3 */\n"
"tag_1:\n"
- " keccak256(0x2, 0x1)\n"
+ " keccak256(0x02, 0x01)\n"
" bytecodeSize\n"
" linkerSymbol(\"bf005014d9d0f534b8fcb268bd84c491a2380f4acd260d1ccfe9cd8201f7e994\")\n"
" jump(tag_1)\n"
diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp
index 7d102948..c65d28d3 100644
--- a/test/libevmasm/Optimiser.cpp
+++ b/test/libevmasm/Optimiser.cpp
@@ -53,7 +53,7 @@ namespace
// add dummy locations to each item so that we can check that they are not deleted
AssemblyItems input = _input;
for (AssemblyItem& item: input)
- item.setLocation(SourceLocation(1, 3, nullptr));
+ item.setLocation({1, 3, nullptr});
return input;
}
diff --git a/test/liblangutil/SourceLocation.cpp b/test/liblangutil/SourceLocation.cpp
index ef4103da..5bdce88e 100644
--- a/test/liblangutil/SourceLocation.cpp
+++ b/test/liblangutil/SourceLocation.cpp
@@ -37,12 +37,12 @@ BOOST_AUTO_TEST_CASE(test_fail)
auto const sourceA = std::make_shared<CharStream>("", "sourceA");
auto const sourceB = std::make_shared<CharStream>("", "sourceB");
- BOOST_CHECK(SourceLocation() == SourceLocation());
- BOOST_CHECK(SourceLocation(0, 3, sourceA) != SourceLocation(0, 3, sourceB));
- BOOST_CHECK(SourceLocation(0, 3, source) == SourceLocation(0, 3, source));
- BOOST_CHECK(SourceLocation(3, 7, source).contains(SourceLocation(4, 6, source)));
- BOOST_CHECK(!SourceLocation(3, 7, sourceA).contains(SourceLocation(4, 6, sourceB)));
- BOOST_CHECK(SourceLocation(3, 7, sourceA) < SourceLocation(4, 6, sourceB));
+ BOOST_CHECK(SourceLocation{} == SourceLocation{});
+ BOOST_CHECK((SourceLocation{0, 3, sourceA} != SourceLocation{0, 3, sourceB}));
+ BOOST_CHECK((SourceLocation{0, 3, source} == SourceLocation{0, 3, source}));
+ BOOST_CHECK((SourceLocation{3, 7, source}.contains(SourceLocation{4, 6, source})));
+ BOOST_CHECK((!SourceLocation{3, 7, sourceA}.contains(SourceLocation{4, 6, sourceB})));
+ BOOST_CHECK((SourceLocation{3, 7, sourceA} < SourceLocation{4, 6, sourceB}));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp
index aad89b91..85ce65a1 100644
--- a/test/liblll/EndToEndTest.cpp
+++ b/test/liblll/EndToEndTest.cpp
@@ -1008,7 +1008,7 @@ BOOST_AUTO_TEST_CASE(sub_assemblies)
compileAndRun(sourceCode);
bytes ret = callFallback();
BOOST_REQUIRE(ret.size() == 32);
- u256 rVal = u256(toHex(ret, 2, HexPrefix::Add));
+ u256 rVal = u256(toHex(ret, HexPrefix::Add));
BOOST_CHECK(rVal != 0);
BOOST_CHECK(rVal < u256("0x10000000000000000000000000000000000000000"));
}
diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp
index bd24115c..abeecd32 100644
--- a/test/libsolidity/AnalysisFramework.cpp
+++ b/test/libsolidity/AnalysisFramework.cpp
@@ -52,7 +52,7 @@ AnalysisFramework::parseAnalyseAndReturnError(
m_compiler.setEVMVersion(dev::test::Options::get().evmVersion());
if (!m_compiler.parse())
{
- BOOST_ERROR("Parsing contract failed in analysis test suite:" + formatErrors());
+ BOOST_FAIL("Parsing contract failed in analysis test suite:" + formatErrors());
}
m_compiler.analyze();
@@ -127,8 +127,7 @@ string AnalysisFramework::formatError(Error const& _error) const
{
return SourceReferenceFormatter::formatExceptionInformation(
_error,
- (_error.type() == Error::Type::Warning) ? "Warning" : "Error",
- [&](std::string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); }
+ (_error.type() == Error::Type::Warning) ? "Warning" : "Error"
);
}
diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp
index aa10147c..baa9bff1 100644
--- a/test/libsolidity/Assembly.cpp
+++ b/test/libsolidity/Assembly.cpp
@@ -165,19 +165,19 @@ BOOST_AUTO_TEST_CASE(location_test)
auto codegenCharStream = make_shared<CharStream>("", "--CODEGEN--");
vector<SourceLocation> locations =
- vector<SourceLocation>(hasShifts ? 21 : 22, SourceLocation(2, 82, sourceCode)) +
- vector<SourceLocation>(2, SourceLocation(20, 79, sourceCode)) +
- vector<SourceLocation>(1, SourceLocation(8, 17, codegenCharStream)) +
- vector<SourceLocation>(3, SourceLocation(5, 7, codegenCharStream)) +
- vector<SourceLocation>(1, SourceLocation(30, 31, codegenCharStream)) +
- vector<SourceLocation>(1, SourceLocation(27, 28, codegenCharStream)) +
- vector<SourceLocation>(1, SourceLocation(20, 32, codegenCharStream)) +
- vector<SourceLocation>(1, SourceLocation(5, 7, codegenCharStream)) +
- vector<SourceLocation>(24, SourceLocation(20, 79, sourceCode)) +
- vector<SourceLocation>(1, SourceLocation(49, 58, sourceCode)) +
- vector<SourceLocation>(1, SourceLocation(72, 74, sourceCode)) +
- vector<SourceLocation>(2, SourceLocation(65, 74, sourceCode)) +
- vector<SourceLocation>(2, SourceLocation(20, 79, sourceCode));
+ vector<SourceLocation>(4, SourceLocation{2, 82, sourceCode}) +
+ vector<SourceLocation>(1, SourceLocation{8, 17, codegenCharStream}) +
+ vector<SourceLocation>(3, SourceLocation{5, 7, codegenCharStream}) +
+ vector<SourceLocation>(1, SourceLocation{30, 31, codegenCharStream}) +
+ vector<SourceLocation>(1, SourceLocation{27, 28, codegenCharStream}) +
+ vector<SourceLocation>(1, SourceLocation{20, 32, codegenCharStream}) +
+ vector<SourceLocation>(1, SourceLocation{5, 7, codegenCharStream}) +
+ vector<SourceLocation>(hasShifts ? 19 : 20, SourceLocation{2, 82, sourceCode}) +
+ vector<SourceLocation>(24, SourceLocation{20, 79, sourceCode}) +
+ vector<SourceLocation>(1, SourceLocation{49, 58, sourceCode}) +
+ vector<SourceLocation>(1, SourceLocation{72, 74, sourceCode}) +
+ vector<SourceLocation>(2, SourceLocation{65, 74, sourceCode}) +
+ vector<SourceLocation>(2, SourceLocation{20, 79, sourceCode});
checkAssemblyLocations(items, locations);
}
diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp
new file mode 100644
index 00000000..c7da3ca0
--- /dev/null
+++ b/test/libsolidity/GasCosts.cpp
@@ -0,0 +1,132 @@
+/*
+ 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/>.
+*/
+/**
+ * Tests that check that the cost of certain operations stay within range.
+ */
+
+#include <test/libsolidity/SolidityExecutionFramework.h>
+
+#include <cmath>
+
+using namespace std;
+using namespace langutil;
+using namespace dev::eth;
+using namespace dev::solidity;
+using namespace dev::test;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+#define CHECK_GAS(_gasNoOpt, _gasOpt, _tolerance) \
+ do \
+ { \
+ u256 gasOpt{_gasOpt}; \
+ u256 gasNoOpt{_gasNoOpt}; \
+ u256 tolerance{_tolerance}; \
+ u256 gas = m_optimize ? gasOpt : gasNoOpt; \
+ u256 diff = gas < m_gasUsed ? m_gasUsed - gas : gas - m_gasUsed; \
+ BOOST_CHECK_MESSAGE( \
+ diff <= tolerance, \
+ "Gas used: " + \
+ m_gasUsed.str() + \
+ " - expected: " + \
+ gas.str() + \
+ " (tolerance: " + \
+ tolerance.str() + \
+ ")" \
+ ); \
+ } while(0)
+
+BOOST_FIXTURE_TEST_SUITE(GasCostTests, SolidityExecutionFramework)
+
+BOOST_AUTO_TEST_CASE(string_storage)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() pure public {
+ require(false, "Not Authorized. This function can only be called by the custodian or owner of this contract");
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+
+ if (Options::get().evmVersion() <= EVMVersion::byzantium())
+ CHECK_GAS(134435, 130591, 100);
+ else
+ CHECK_GAS(127225, 124873, 100);
+ if (Options::get().evmVersion() >= EVMVersion::byzantium())
+ {
+ callContractFunction("f()");
+ if (Options::get().evmVersion() == EVMVersion::byzantium())
+ CHECK_GAS(21551, 21526, 20);
+ else
+ CHECK_GAS(21546, 21526, 20);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(single_callvaluecheck)
+{
+ string sourceCode = R"(
+ // All functions nonpayable, we can check callvalue at the beginning
+ contract Nonpayable {
+ address a;
+ function f(address b) public {
+ a = b;
+ }
+ function f1(address b) public pure returns (uint c) {
+ return uint(b) + 2;
+ }
+ function f2(address b) public pure returns (uint) {
+ return uint(b) + 8;
+ }
+ function f3(address, uint c) pure public returns (uint) {
+ return c - 5;
+ }
+ }
+ // At least on payable function, we cannot do the optimization.
+ contract Payable {
+ address a;
+ function f(address b) public {
+ a = b;
+ }
+ function f1(address b) public pure returns (uint c) {
+ return uint(b) + 2;
+ }
+ function f2(address b) public pure returns (uint) {
+ return uint(b) + 8;
+ }
+ function f3(address, uint c) payable public returns (uint) {
+ return c - 5;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ size_t bytecodeSizeNonpayable = m_compiler.object("Nonpayable").bytecode.size();
+ size_t bytecodeSizePayable = m_compiler.object("Payable").bytecode.size();
+
+ BOOST_CHECK_EQUAL(bytecodeSizePayable - bytecodeSizeNonpayable, 26);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp
index 601948bc..d765d440 100644
--- a/test/libsolidity/GasMeter.cpp
+++ b/test/libsolidity/GasMeter.cpp
@@ -44,7 +44,6 @@ namespace test
class GasMeterTestFramework: public SolidityExecutionFramework
{
public:
- GasMeterTestFramework() { }
void compile(string const& _sourceCode)
{
m_compiler.reset(false);
@@ -62,7 +61,7 @@ public:
);
}
- void testCreationTimeGas(string const& _sourceCode)
+ void testCreationTimeGas(string const& _sourceCode, u256 const& _tolerance = u256(0))
{
compileAndRun(_sourceCode);
auto state = make_shared<KnownState>();
@@ -75,12 +74,13 @@ public:
gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true);
BOOST_REQUIRE(!gas.isInfinite);
- BOOST_CHECK_EQUAL(gas.value, m_gasUsed);
+ BOOST_CHECK_LE(m_gasUsed, gas.value);
+ BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed);
}
/// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments)
/// against the actual gas usage computed by the VM on the given set of argument variants.
- void testRunTimeGas(string const& _sig, vector<bytes> _argumentVariants)
+ void testRunTimeGas(string const& _sig, vector<bytes> _argumentVariants, u256 const& _tolerance = u256(0))
{
u256 gasUsed = 0;
GasMeter::GasConsumption gas;
@@ -98,7 +98,8 @@ public:
_sig
);
BOOST_REQUIRE(!gas.isInfinite);
- BOOST_CHECK_EQUAL(gas.value, m_gasUsed);
+ BOOST_CHECK_LE(m_gasUsed, gas.value);
+ BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed);
}
static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation)
@@ -138,8 +139,7 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs)
if (first->first->location().intersects(second->first->location()))
{
BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!");
- auto scannerFromSource = [&](string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); };
- SourceReferenceFormatter formatter(cout, scannerFromSource);
+ SourceReferenceFormatter formatter(cout);
formatter.printSourceLocation(&first->first->location());
formatter.printSourceLocation(&second->first->location());
@@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(updating_store)
}
}
)";
- testCreationTimeGas(sourceCode);
+ testCreationTimeGas(sourceCode, m_evmVersion < EVMVersion::constantinople() ? u256(0) : u256(9600));
}
BOOST_AUTO_TEST_CASE(branches)
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index 11d4c59f..b6986041 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -433,8 +433,7 @@ BOOST_AUTO_TEST_CASE(variable_access_cross_functions)
BOOST_AUTO_TEST_CASE(invalid_tuple_assignment)
{
- /// The push(42) is added here to silence the unbalanced stack error, so that there's only one error reported.
- CHECK_PARSE_ERROR("{ 42 let x, y := 1 }", DeclarationError, "Variable count mismatch.");
+ CHECK_PARSE_ERROR("{ let x, y := 1 }", DeclarationError, "Variable count mismatch: 2 variables and 1 values");
}
BOOST_AUTO_TEST_CASE(instruction_too_few_arguments)
diff --git a/test/libsolidity/LibSolc.cpp b/test/libsolidity/LibSolc.cpp
index 09c08700..ec97f22f 100644
--- a/test/libsolidity/LibSolc.cpp
+++ b/test/libsolidity/LibSolc.cpp
@@ -81,7 +81,9 @@ BOOST_AUTO_TEST_CASE(standard_compilation)
// Only tests some assumptions. The StandardCompiler is tested properly in another suite.
BOOST_CHECK(result.isMember("sources"));
- BOOST_CHECK(result.isMember("contracts"));
+ // This used to test that it is a member, but we did not actually request any output,
+ // so there should not be a contract member.
+ BOOST_CHECK(!result.isMember("contracts"));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index a8a67bca..63f2b3a6 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -38,8 +38,6 @@ namespace test
class JSONInterfaceChecker
{
public:
- JSONInterfaceChecker(): m_compilerStack() {}
-
void checkInterface(std::string const& _code, std::string const& _contractName, std::string const& _expectedInterfaceString)
{
m_compilerStack.reset(false);
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index c6135a72..8d219d16 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -3039,7 +3039,8 @@ BOOST_AUTO_TEST_CASE(gaslimit)
}
)";
compileAndRun(sourceCode);
- ABI_CHECK(callContractFunction("f()"), encodeArgs(gasLimit()));
+ auto result = callContractFunction("f()");
+ ABI_CHECK(result, encodeArgs(gasLimit()));
}
BOOST_AUTO_TEST_CASE(gasprice)
@@ -12245,7 +12246,7 @@ BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once)
compileAndRun(sourceCode);
BOOST_CHECK_LE(
double(m_compiler.object("Double").bytecode.size()),
- 1.1 * double(m_compiler.object("Single").bytecode.size())
+ 1.2 * double(m_compiler.object("Single").bytecode.size())
);
}
diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h
index cedbf51a..73377eb9 100644
--- a/test/libsolidity/SolidityExecutionFramework.h
+++ b/test/libsolidity/SolidityExecutionFramework.h
@@ -72,8 +72,7 @@ public:
m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns);
if (!m_compiler.compile())
{
- auto scannerFromSourceName = [&](std::string const& _sourceName) -> langutil::Scanner const& { return m_compiler.scanner(_sourceName); };
- langutil::SourceReferenceFormatter formatter(std::cerr, scannerFromSourceName);
+ langutil::SourceReferenceFormatter formatter(std::cerr);
for (auto const& error: m_compiler.errors())
formatter.printExceptionInformation(
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
index 8bce26c1..f5c4dc5e 100644
--- a/test/libsolidity/SolidityExpressionCompiler.cpp
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(unary_operators)
{
char const* sourceCode = R"(
contract test {
- function f(int y) { !(~+- y == 2); }
+ function f(int y) { !(~- y == 2); }
}
)";
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 75726027..774f67fe 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -376,18 +376,6 @@ BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma)
BOOST_CHECK(searchErrorMessage(*sourceAndError.second.front(), "Source file does not specify required compiler version!"));
}
-BOOST_AUTO_TEST_CASE(unsatisfied_version)
-{
- char const* text = R"(
- pragma solidity ^99.99.0;
- )";
- auto sourceAndError = parseAnalyseAndReturnError(text, false, false, false);
- BOOST_REQUIRE(!sourceAndError.second.empty());
- BOOST_REQUIRE(!!sourceAndError.first);
- BOOST_CHECK(sourceAndError.second.front()->type() == Error::Type::SyntaxError);
- BOOST_CHECK(searchErrorMessage(*sourceAndError.second.front(), "Source file requires different compiler version"));
-}
-
BOOST_AUTO_TEST_CASE(returndatasize_as_variable)
{
char const* text = R"(
diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp
index d930f697..000a7938 100644
--- a/test/libsolidity/SolidityNatspecJSON.cpp
+++ b/test/libsolidity/SolidityNatspecJSON.cpp
@@ -39,8 +39,6 @@ namespace test
class DocumentationChecker
{
public:
- DocumentationChecker(): m_compilerStack() {}
-
void checkNatspec(
std::string const& _code,
std::string const& _contractName,
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
index f925d36e..b5ce6f2a 100644
--- a/test/libsolidity/SolidityOptimizer.cpp
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -45,8 +45,6 @@ namespace test
class OptimizerTestFramework: public SolidityExecutionFramework
{
public:
- OptimizerTestFramework() { }
-
bytes const& compileAndRunWithOptimizer(
std::string const& _sourceCode,
u256 const& _value = 0,
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index d86d3d39..a33c6134 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -113,6 +113,23 @@ while(0)
BOOST_AUTO_TEST_SUITE(SolidityParser)
+BOOST_AUTO_TEST_CASE(unsatisfied_version)
+{
+ char const* text = R"(
+ pragma solidity ^99.99.0;
+ )";
+ CHECK_PARSE_ERROR(text, "Source file requires different compiler version");
+}
+
+BOOST_AUTO_TEST_CASE(unsatisfied_version_followed_by_invalid_syntax)
+{
+ char const* text = R"(
+ pragma solidity ^99.99.0;
+ this is surely invalid
+ )";
+ CHECK_PARSE_ERROR(text, "Source file requires different compiler version");
+}
+
BOOST_AUTO_TEST_CASE(function_natspec_documentation)
{
char const* text = R"(
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 1570a9d2..118d8210 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -333,11 +333,11 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
" /* \"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 "
+ "/* \"--CODEGEN--\":30:31 */\n 0x00\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 "
+ "dataOffset(sub_0)\n 0x00\n codecopy\n 0x00\n return\nstop\n\nsub_0: assembly {\n "
+ "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n 0x00\n "
"dup1\n revert\n\n auxdata: 0xa165627a7a72305820"
) == 0);
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol
index 3793f411..2ecff63e 100644
--- a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol
+++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol
@@ -12,3 +12,4 @@ contract C
}
// ----
+// Warning: (153-156): Assertion checker does not yet implement tuples and inline arrays.
diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol
index e3c80594..a9bde9e4 100644
--- a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol
+++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol
@@ -12,4 +12,5 @@ contract C
}
// ----
+// Warning: (153-156): Assertion checker does not yet implement tuples and inline arrays.
// Warning: (163-176): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol
new file mode 100644
index 00000000..df6eaaa7
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol
@@ -0,0 +1,17 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(uint x) public pure {
+ require(x < 100);
+ do {
+ // Overflows due to resetting x.
+ x = x + 1;
+ } while (x < 10);
+ assert(x < 14);
+ }
+}
+// ----
+// Warning: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (146-155): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (179-193): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol
new file mode 100644
index 00000000..49a1e0a5
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol
@@ -0,0 +1,19 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(uint x) public pure {
+ require(x < 100);
+ do {
+ // Overflows due to resetting x.
+ x = x + 1;
+ } while (x < 1000);
+ // The assertion is true but we can't infer so
+ // because x is touched in the loop.
+ assert(x > 0);
+ }
+}
+// ----
+// Warning: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (146-155): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (269-282): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol
new file mode 100644
index 00000000..2be01c2d
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol
@@ -0,0 +1,17 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(uint x) public pure {
+ require(x < 100);
+ for(uint i = 0; i < 10; ++i) {
+ // Overflows due to resetting x.
+ x = x + 1;
+ }
+ assert(x < 14);
+ }
+}
+// ----
+// Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (172-181): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (189-203): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol
new file mode 100644
index 00000000..c8232ab6
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol
@@ -0,0 +1,18 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(uint x) public pure {
+ require(x < 100);
+ for(uint i = 0; i < 10; ++i) {
+ // Overflows due to resetting x.
+ x = x + 1;
+ }
+ // The assertion is true but x is touched and reset.
+ assert(x > 0);
+ }
+}
+// ----
+// Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (172-181): Overflow (resulting value larger than 2**256 - 1) happens here
+// Warning: (244-257): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol
index 2c84960f..eb62d36e 100644
--- a/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol
+++ b/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol
@@ -9,4 +9,4 @@ contract C {
}
}
// ----
-// Warning: (167-181): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require().
+// Warning: (167-181): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol
index 90c4c328..b0c3cae4 100644
--- a/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol
+++ b/test/libsolidity/smtCheckerTests/loops/for_loop_6.sol
@@ -5,8 +5,9 @@ contract C {
for (y = 2; x < 10; ) {
y = 3;
}
- assert(y == 2);
+ // False positive due to resetting y.
+ assert(y < 4);
}
}
// ----
-// Warning: (167-181): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require().
+// Warning: (213-226): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol
new file mode 100644
index 00000000..21e6c91e
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol
@@ -0,0 +1,10 @@
+pragma experimental SMTChecker;
+contract C {
+ function f(uint x) public pure {
+ require(x == 2);
+ for (; x == 2;) {}
+ assert(x == 2);
+ }
+}
+// ----
+// Warning: (122-128): For loop condition is always true.
diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol
new file mode 100644
index 00000000..6184c441
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol
@@ -0,0 +1,13 @@
+pragma experimental SMTChecker;
+contract C {
+ function f(uint x) public pure {
+ require(x == 2);
+ uint y;
+ for (; x == 2;) {
+ y = 7;
+ }
+ assert(x == 2);
+ }
+}
+// ----
+// Warning: (138-144): For loop condition is always true.
diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_3.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_3.sol
new file mode 100644
index 00000000..eec59ded
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_3.sol
@@ -0,0 +1,18 @@
+pragma experimental SMTChecker;
+contract C {
+ function f(uint x) public pure {
+ require(x == 2);
+ uint y;
+ // The loop condition is always true,
+ // but since x is touched in the body
+ // we can't infer that.
+ for (; x == 2;) {
+ x = 2;
+ }
+ // False positive due to resetting x.
+ assert(x == 2);
+ }
+}
+// ----
+// Warning: (115-121): Unused local variable.
+// Warning: (356-370): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol
new file mode 100644
index 00000000..f367d8d9
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol
@@ -0,0 +1,10 @@
+pragma experimental SMTChecker;
+contract C {
+ function f(uint x) public pure {
+ require(x == 2);
+ for (; x > 2;) {}
+ assert(x == 2);
+ }
+}
+// ----
+// Warning: (122-127): For loop condition is always false.
diff --git a/test/libsolidity/smtCheckerTests/loops/while_1.sol b/test/libsolidity/smtCheckerTests/loops/while_1.sol
new file mode 100644
index 00000000..871ed929
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/while_1.sol
@@ -0,0 +1,17 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(uint x, bool b) public pure {
+ require(x < 100);
+ while (x < 10) {
+ if (b)
+ x = x + 1;
+ else
+ x = 0;
+ }
+ assert(x > 0);
+ }
+}
+// ----
+// Warning: (177-190): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol
new file mode 100644
index 00000000..6964f7c8
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol
@@ -0,0 +1,14 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(uint x) public pure {
+ require(x < 100);
+ while (x < 10) {
+ x = x + 1;
+ }
+ assert(x < 14);
+ }
+}
+// ----
+// Warning: (139-153): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/while_2_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_2_fail.sol
new file mode 100644
index 00000000..4c52287d
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/loops/while_2_fail.sol
@@ -0,0 +1,15 @@
+pragma experimental SMTChecker;
+contract C {
+ function f(uint x) public pure {
+ x = 2;
+ while (x > 1) {
+ if (x > 10)
+ x = 2;
+ else
+ x = 10;
+ }
+ assert(x == 2);
+ }
+}
+// ----
+// Warning: (158-172): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol
index 074be86f..9b5d04c3 100644
--- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol
+++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol
@@ -4,10 +4,10 @@ contract C {
function f(uint x) public pure {
x = 2;
while (x > 1) {
- x = 2;
+ x = 1;
}
assert(x == 2);
}
}
// ----
-// Warning: (194-208): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require().
+// Warning: (194-208): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol
index a37df888..f3634edb 100644
--- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol
+++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol
@@ -8,4 +8,4 @@ contract C {
}
}
// ----
-// Warning: (187-201): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require().
+// Warning: (187-201): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol
index f71da865..5a3aee9e 100644
--- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol
+++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_4.sol
@@ -8,4 +8,4 @@ contract C {
}
}
// ----
-// Warning: (199-213): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require().
+// Warning: (199-213): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol
index 41559c99..6c81e36f 100644
--- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol
+++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol
@@ -9,4 +9,4 @@ contract C {
}
}
// ----
-// Warning: (216-230): Assertion violation happens here\nNote that some information is erased after the execution of loops.\nYou can re-introduce information using require().
+// Warning: (216-230): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol
index 894ff1a4..ec819b80 100644
--- a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol
+++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol
@@ -3,4 +3,4 @@ contract C {
function f(uint a, uint b) public pure returns (uint) { return a + b; }
}
// ----
-// Warning: (112-117): Overflow (resulting value larger than 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) happens here
+// Warning: (112-117): Overflow (resulting value larger than 2**256 - 1) happens here
diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol
index 40e5d987..ae60b1e5 100644
--- a/test/libsolidity/smtCheckerTests/special/many.sol
+++ b/test/libsolidity/smtCheckerTests/special/many.sol
@@ -20,6 +20,6 @@ contract C
// Warning: (165-204): Assertion violation happens here
// Warning: (208-240): Assertion violation happens here
// Warning: (244-275): Assertion violation happens here
-// Warning: (311-316): Overflow (resulting value larger than 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) happens here
+// Warning: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here
// Warning: (336-352): Assertion violation happens here
// Warning: (356-379): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol
index 7e748f09..b667f971 100644
--- a/test/libsolidity/smtCheckerTests/special/msg_data.sol
+++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol
@@ -8,7 +8,6 @@ contract C
}
// ----
// Warning: (86-101): Assertion checker does not yet support this expression.
-// Warning: (86-94): Assertion checker does not yet support this special variable.
-// Warning: (86-94): Assertion checker does not yet implement this type.
+// Warning: (86-94): Assertion checker does not yet support this global variable.
// Warning: (86-101): Internal error: Expression undefined for SMT solver.
// Warning: (79-106): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/bytes_1.sol b/test/libsolidity/smtCheckerTests/types/bytes_1.sol
new file mode 100644
index 00000000..fb957421
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/bytes_1.sol
@@ -0,0 +1,16 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ function f(bytes memory b) public pure returns (bytes memory) {
+ bytes memory c = b;
+ return b;
+ }
+}
+// ----
+// Warning: (113-127): Unused local variable.
+// Warning: (113-127): Assertion checker does not yet support the type of this variable.
+// Warning: (58-72): Assertion checker does not yet support the type of this variable.
+// Warning: (95-107): Assertion checker does not yet support the type of this variable.
+// Warning: (130-131): Internal error: Expression undefined for SMT solver.
+// Warning: (130-131): Assertion checker does not yet implement this type.
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_1.sol
new file mode 100644
index 00000000..4d71ff38
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_1.sol
@@ -0,0 +1,10 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => uint) map;
+ function f(uint x) public {
+ map[2] = x;
+ assert(x == map[2]);
+ }
+}
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol
new file mode 100644
index 00000000..83c963ad
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol
@@ -0,0 +1,13 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => uint) map;
+ function f(uint x) public {
+ map[2] = x;
+ map[2] = 3;
+ assert(x != map[2]);
+ }
+}
+// ----
+// Warning: (134-153): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_2.sol
new file mode 100644
index 00000000..06d618bd
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_2.sol
@@ -0,0 +1,11 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => bool) map;
+ function f(bool x) public view {
+ assert(x != map[2]);
+ }
+}
+// ----
+// Warning: (111-130): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol
new file mode 100644
index 00000000..b6474903
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_2d_1.sol
@@ -0,0 +1,14 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => mapping (uint => uint)) map;
+ function f(uint x) public {
+ x = 42;
+ map[13][14] = 42;
+ assert(x == map[13][14]);
+ }
+}
+// ----
+// Warning: (134-145): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays.
+// Warning: (154-178): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol
new file mode 100644
index 00000000..dd4d568e
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol
@@ -0,0 +1,14 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => mapping (uint => uint)) map;
+ function f(uint x) public {
+ x = 41;
+ map[13][14] = 42;
+ assert(x == map[13][14]);
+ }
+}
+// ----
+// Warning: (134-145): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays.
+// Warning: (154-178): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_3.sol b/test/libsolidity/smtCheckerTests/types/mapping_3.sol
new file mode 100644
index 00000000..985ed3a3
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_3.sol
@@ -0,0 +1,12 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => uint) map;
+ function f() public {
+ map[1] = 111;
+ uint x = map[2];
+ map[1] = 112;
+ assert(map[2] == x);
+ }
+}
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol
new file mode 100644
index 00000000..6c5f439a
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_3d_1.sol
@@ -0,0 +1,14 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => mapping (uint => mapping (uint => uint))) map;
+ function f(uint x) public {
+ x = 42;
+ map[13][14][15] = 42;
+ assert(x == map[13][14][15]);
+ }
+}
+// ----
+// Warning: (152-167): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays.
+// Warning: (176-204): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol
new file mode 100644
index 00000000..dfd4ddaf
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol
@@ -0,0 +1,14 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => mapping (uint => mapping (uint => uint))) map;
+ function f(uint x) public {
+ x = 41;
+ map[13][14][15] = 42;
+ assert(x == map[13][14][15]);
+ }
+}
+// ----
+// Warning: (152-167): Assertion checker does not yet implement assignments to multi-dimensional mappings or arrays.
+// Warning: (176-204): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_4.sol b/test/libsolidity/smtCheckerTests/types/mapping_4.sol
new file mode 100644
index 00000000..d0204211
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_4.sol
@@ -0,0 +1,12 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (bool => bool) map;
+ function f(bool x) public view {
+ require(x);
+ assert(x != map[x]);
+ }
+}
+// ----
+// Warning: (125-144): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_5.sol b/test/libsolidity/smtCheckerTests/types/mapping_5.sol
new file mode 100644
index 00000000..4acea501
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_5.sol
@@ -0,0 +1,11 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (address => uint) map;
+ function f(address a, uint x) public view {
+ assert(x != map[a]);
+ }
+}
+// ----
+// Warning: (125-144): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol
new file mode 100644
index 00000000..39d096f5
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol
@@ -0,0 +1,18 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => uint) a;
+ mapping (uint => uint) b;
+
+ function f() public {
+ require(a[1] == b[1]);
+ a[1] = 2;
+ mapping (uint => uint) storage c = a;
+ assert(c[1] == 2);
+ // False negative! Needs aliasing.
+ assert(a[1] == b[1]);
+ }
+}
+// ----
+// Warning: (261-281): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol
new file mode 100644
index 00000000..e005fbef
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol
@@ -0,0 +1,21 @@
+pragma experimental SMTChecker;
+
+contract c {
+ mapping(uint => uint) x;
+ mapping(uint => uint) y;
+ function f(bool cond) public {
+ mapping(uint => uint) storage a = cond ? x : y;
+ x[2] = 1;
+ y[2] = 2;
+ a[2] = 3;
+ // False positive since aliasing is not yet supported.
+ if (cond)
+ assert(a[2] == x[2] && a[2] != y[2]);
+ else
+ assert(a[2] == y[2] && a[2] != x[2]);
+ }
+}
+// ----
+// Warning: (166-178): Internal error: Expression undefined for SMT solver.
+// Warning: (288-324): Assertion violation happens here
+// Warning: (336-372): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol
new file mode 100644
index 00000000..9aeed32b
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol
@@ -0,0 +1,15 @@
+pragma experimental SMTChecker;
+
+contract c {
+ mapping(uint => uint) x;
+ function f(mapping(uint => uint) storage map, uint index, uint value) internal {
+ map[index] = value;
+ }
+ function g(uint a, uint b) public {
+ f(x, a, b);
+ // False positive since aliasing is not yet supported.
+ assert(x[a] == b);
+ }
+}
+// ----
+// Warning: (289-306): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_1.sol
new file mode 100644
index 00000000..188bc59a
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_1.sol
@@ -0,0 +1,10 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => uint) map;
+ function f(uint x, uint y) public view {
+ require(x == y);
+ assert(map[x] == map[y]);
+ }
+}
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol
new file mode 100644
index 00000000..93b249df
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol
@@ -0,0 +1,12 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (uint => uint) map;
+ function f(uint x, uint y) public view {
+ assert(x == y);
+ assert(map[x] == map[y]);
+ }
+}
+// ----
+// Warning: (119-133): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTests/types/mapping_unsupported_key_type_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_unsupported_key_type_1.sol
new file mode 100644
index 00000000..f4e3a65f
--- /dev/null
+++ b/test/libsolidity/smtCheckerTests/types/mapping_unsupported_key_type_1.sol
@@ -0,0 +1,17 @@
+pragma experimental SMTChecker;
+
+contract C
+{
+ mapping (string => uint) map;
+ function f(string memory s, uint x) public {
+ map[s] = x;
+ assert(x == map[s]);
+ }
+}
+// ----
+// Warning: (89-104): Assertion checker does not yet support the type of this variable.
+// Warning: (129-130): Internal error: Expression undefined for SMT solver.
+// Warning: (129-130): Assertion checker does not yet implement this type.
+// Warning: (155-156): Internal error: Expression undefined for SMT solver.
+// Warning: (155-156): Assertion checker does not yet implement this type.
+// Warning: (139-158): Assertion violation happens here
diff --git a/test/libsolidity/smtCheckerTestsJSON/multi.json b/test/libsolidity/smtCheckerTestsJSON/multi.json
index 2ed5150d..df45fbf4 100644
--- a/test/libsolidity/smtCheckerTestsJSON/multi.json
+++ b/test/libsolidity/smtCheckerTestsJSON/multi.json
@@ -1,11 +1,11 @@
{
- "auxiliaryInput":
- {
- "smtlib2responses":
- {
- "0x0426cd198d1e7123a28ffac2b759a666b86508ad046babf5166500dd6d8ed308": "unsat\n(error \"line 31 column 26: model is not available\")",
- "0xa51ca41ae407f5a727f27101cbc079834743cc8955f9f585582034ca634953f6": "sat\n((|EVALEXPR_0| 1))",
- "0xe9477f683ff20aa57fcb08682150f86c5917e1d4c0686b278ab9b73446d0682c": "sat\n((|EVALEXPR_0| 0))"
- }
- }
+ "auxiliaryInput":
+ {
+ "smtlib2responses":
+ {
+ "0x47826ec8b83cfec30171b34f944aed6c33ecd12f02b9ad522ca45317c9bd5f45": "sat\n((|EVALEXPR_0| 1))",
+ "0x734c058efe9d6ec224de3cf2bdfa6c936a1c9c159c040c128d631145b2fed195": "unsat\n",
+ "0xa0c072acdbe5181dd56cbad8960cb5bb0b9e97fd598cfd895467bd73bbcca028": "sat\n((|EVALEXPR_0| 0))"
+ }
+ }
}
diff --git a/test/libsolidity/smtCheckerTestsJSON/simple.json b/test/libsolidity/smtCheckerTestsJSON/simple.json
index fd976b63..b82d93b8 100644
--- a/test/libsolidity/smtCheckerTestsJSON/simple.json
+++ b/test/libsolidity/smtCheckerTestsJSON/simple.json
@@ -1,9 +1,9 @@
{
- "auxiliaryInput":
- {
- "smtlib2responses":
- {
- "0xe9477f683ff20aa57fcb08682150f86c5917e1d4c0686b278ab9b73446d0682c": "sat\n((|EVALEXPR_0| 0))"
- }
- }
+ "auxiliaryInput":
+ {
+ "smtlib2responses":
+ {
+ "0xa0c072acdbe5181dd56cbad8960cb5bb0b9e97fd598cfd895467bd73bbcca028": "sat\n((|EVALEXPR_0| 0))\n"
+ }
+ }
}
diff --git a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol
index 35420b6d..dbd3db08 100644
--- a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol
@@ -2,4 +2,4 @@ contract C {
function f() internal pure returns (mapping(uint=>uint) storage r) { }
}
// ----
-// TypeError: (53-82): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (53-82): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol
index 52a8b3d7..476e799b 100644
--- a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol
@@ -2,4 +2,4 @@ contract C {
function f() internal pure returns (mapping(uint=>uint) storage) {}
}
// ----
-// TypeError: (53-80): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (53-80): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol
index cad9b8e8..a6df889d 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol
@@ -7,4 +7,4 @@ contract C {
}
}
// ----
-// TypeError: (87-96): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (87-96): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol
deleted file mode 100644
index 0d3db856..00000000
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol
+++ /dev/null
@@ -1,26 +0,0 @@
-contract C {
- struct S { bool f; }
- S s;
- function f() internal returns (S storage c) {
- assembly {
- sstore(c_slot, sload(s_slot))
- }
- }
- function g(bool flag) internal returns (S storage c) {
- // control flow in assembly will not be analyzed for now,
- // so this will not issue an error
- assembly {
- if flag {
- sstore(c_slot, sload(s_slot))
- }
- }
- }
- function h() internal returns (S storage c) {
- // any reference from assembly will be sufficient for now,
- // so this will not issue an error
- assembly {
- sstore(s_slot, sload(c_slot))
- }
- }
-}
-// ----
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol
index eb574c96..b868d61d 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol
@@ -45,8 +45,8 @@ contract C {
}
}
// ----
-// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (223-234): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (440-451): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (654-665): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (871-882): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (223-234): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (440-451): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (654-665): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (871-882): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol
index 9aa580a4..d2dec9c1 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol
@@ -12,5 +12,5 @@ contract C {
}
}
// ----
-// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (182-193): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (182-193): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol
index f3e55318..12ec31e4 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol
@@ -14,5 +14,5 @@ contract C {
}
}
// ----
-// TypeError: (96-107): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (186-197): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (186-197): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol
index 42342979..ee1e08fd 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol
@@ -18,5 +18,5 @@ contract C {
}
}
// ----
-// TypeError: (249-258): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (367-376): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (249-258): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (367-376): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol
index d0ad8245..e3579628 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol
@@ -13,6 +13,6 @@ contract C {
}
}
// ----
-// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (176-187): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (264-275): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (176-187): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (264-275): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol
index 6d10287b..d5ad73c5 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol
@@ -9,5 +9,5 @@ contract C {
}
}
// ----
-// TypeError: (96-107): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
-// TypeError: (200-211): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (96-107): This variable is of storage pointer type and can be returned without prior assignment.
+// TypeError: (200-211): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol
index e7b4fae7..aabb76dd 100644
--- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol
+++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol
@@ -8,4 +8,4 @@ contract C {
}
}
// ----
-// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.
+// TypeError: (87-98): This variable is of storage pointer type and can be returned without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol
new file mode 100644
index 00000000..96767402
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/always_revert.sol
@@ -0,0 +1,8 @@
+contract C {
+ function f() internal view returns(uint[] storage a)
+ {
+ uint b = a[0];
+ revert();
+ b;
+ }
+} \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol
new file mode 100644
index 00000000..bfcbbef5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/assembly.sol
@@ -0,0 +1,9 @@
+contract C {
+ uint[] r;
+ function f() internal view returns (uint[] storage s) {
+ assembly { pop(s_slot) }
+ s = r;
+ }
+}
+// ----
+// TypeError: (92-126): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/functionType.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/functionType.sol
new file mode 100644
index 00000000..1d683eff
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/functionType.sol
@@ -0,0 +1,8 @@
+contract C {
+ // Make sure function parameters and return values are not considered
+ // for uninitialized return detection in the control flow analysis.
+ function f(function(uint[] storage) internal returns (uint[] storage)) internal pure
+ returns (function(uint[] storage) internal returns (uint[] storage))
+ {
+ }
+}
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fail.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fail.sol
new file mode 100644
index 00000000..90d228fa
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fail.sol
@@ -0,0 +1,8 @@
+contract C {
+ modifier m1(uint[] storage a) { _; }
+ modifier m2(uint[] storage a) { _; }
+ uint[] s;
+ function f() m1(b) m2(b = s) internal view returns (uint[] storage b) {}
+}
+// ----
+// TypeError: (129-130): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fine.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fine.sol
new file mode 100644
index 00000000..af133929
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_order_fine.sol
@@ -0,0 +1,6 @@
+contract C {
+ modifier m1(uint[] storage a) { _; }
+ modifier m2(uint[] storage a) { _; }
+ uint[] s;
+ function f() m1(b = s) m2(b) internal view returns (uint[] storage b) {}
+} \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_post_access.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_post_access.sol
new file mode 100644
index 00000000..b9f08615
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_post_access.sol
@@ -0,0 +1,13 @@
+contract C {
+ uint[] s;
+ modifier mod(uint[] storage b) {
+ _;
+ b[0] = 0;
+ }
+ function f() mod(a) internal returns (uint[] storage a)
+ {
+ a = s;
+ }
+}
+// ----
+// TypeError: (120-121): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_pre_access.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_pre_access.sol
new file mode 100644
index 00000000..81618aec
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/modifier_pre_access.sol
@@ -0,0 +1,13 @@
+contract C {
+ uint[] s;
+ modifier mod(uint[] storage b) {
+ b[0] = 0;
+ _;
+ }
+ function f() mod(a) internal returns (uint[] storage a)
+ {
+ a = s;
+ }
+}
+// ----
+// TypeError: (120-121): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/smoke.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/smoke.sol
new file mode 100644
index 00000000..41ced51d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/smoke.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint[] s;
+ function f() internal returns (uint[] storage a)
+ {
+ a[0] = 0;
+ a = s;
+ }
+}
+// ----
+// TypeError: (94-95): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/struct.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/struct.sol
new file mode 100644
index 00000000..b4f2be5d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/struct.sol
@@ -0,0 +1,11 @@
+contract C {
+ struct S { uint a; }
+ S s;
+ function f() internal returns (S storage r)
+ {
+ r.a = 0;
+ r = s;
+ }
+}
+// ----
+// TypeError: (109-110): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol
new file mode 100644
index 00000000..b941ad34
--- /dev/null
+++ b/test/libsolidity/syntaxTests/controlFlow/uninitializedAccess/unreachable.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint[] s;
+ function f() internal returns (uint[] storage a)
+ {
+ revert();
+ a[0] = 0;
+ a = s;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol
index 7a276f95..95d4e02a 100644
--- a/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol
+++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol
@@ -8,3 +8,9 @@ library L {
function i(uint[] calldata, uint[] storage) external pure returns (S storage x) {return x; }
}
// ----
+// TypeError: (197-198): This variable is of storage pointer type and can be accessed without prior assignment.
+// TypeError: (203-204): This variable is of storage pointer type and can be accessed without prior assignment.
+// TypeError: (359-360): This variable is of storage pointer type and can be accessed without prior assignment.
+// TypeError: (365-366): This variable is of storage pointer type and can be accessed without prior assignment.
+// TypeError: (460-461): This variable is of storage pointer type and can be accessed without prior assignment.
+// TypeError: (557-558): This variable is of storage pointer type and can be accessed without prior assignment.
diff --git a/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol
index e7d2c9a9..65198706 100644
--- a/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol
+++ b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol
@@ -18,9 +18,3 @@ contract test {
function(uint) pure internal h = fh;
}
// ----
-// Warning: (20-47): Function state mutability can be restricted to pure
-// Warning: (52-81): Function state mutability can be restricted to pure
-// Warning: (86-115): Function state mutability can be restricted to pure
-// Warning: (120-149): Function state mutability can be restricted to pure
-// Warning: (154-183): Function state mutability can be restricted to pure
-// Warning: (188-217): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol
index ac1f541e..2d36bedd 100644
--- a/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol
+++ b/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol
@@ -10,7 +10,5 @@ contract C {
}
}
// ----
-// TypeError: (87-88): Expected 1 arguments but got 0.
-// SyntaxError: (87-90): Top-level expressions are not supposed to return values (this expression returns -1 values). Use ``pop()`` or assign them.
-// TypeError: (108-109): Expected 1 arguments but got 2.
-// SyntaxError: (108-115): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
+// TypeError: (87-88): Function expects 1 arguments but got 0.
+// TypeError: (108-109): Function expects 1 arguments but got 2.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol
index e112e16c..a554d3ab 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol
@@ -3,4 +3,3 @@ contract test {
function f() public {}
}
// ----
-// Warning: (53-75): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol
index abe2beac..3872b1c3 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol
@@ -3,4 +3,3 @@ contract test {
function g(uint256, bool) public returns (uint256) { }
}
// ----
-// Warning: (88-142): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol
index ec72adeb..bda0b946 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol
@@ -7,5 +7,3 @@ contract Test {
function g (C c) external {}
}
// ----
-// Warning: (125-128): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (113-141): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol
index aedc7b0b..5a9616ac 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol
@@ -9,5 +9,3 @@ contract Test {
}
}
// ----
-// Warning: (68-71): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (56-82): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol
index c07e59e2..2e235ee0 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol
@@ -5,5 +5,3 @@ contract derived is root, inter2, inter1 {
function g() public { f(); rootFunction(); }
}
// ----
-// Warning: (16-49): Function state mutability can be restricted to pure
-// Warning: (129-151): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol
index 062507ee..bf6459cb 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol
@@ -1,6 +1,3 @@
contract B { function f() public {} }
contract C is B { function f(uint i) public {} }
// ----
-// Warning: (67-73): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (13-35): Function state mutability can be restricted to pure
-// Warning: (56-84): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol
index f59da472..461bbbf2 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol
@@ -2,6 +2,3 @@ contract A { function f(uint a) public {} }
contract B { function f() public {} }
contract C is A, B { }
// ----
-// Warning: (24-30): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (13-41): Function state mutability can be restricted to pure
-// Warning: (57-79): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol
index c7e42238..ce3b622b 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol
@@ -3,4 +3,3 @@ contract B { function f() public {} function g() public returns (uint8) {} }
contract C is A, B { }
// ----
// Warning: (35-42): Unused local variable.
-// Warning: (95-133): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol
index 8ebb46aa..31be70ca 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol
@@ -1,4 +1,3 @@
contract A { constructor(uint a) public { } }
contract B is A { }
// ----
-// Warning: (25-31): Unused function parameter. Remove or comment out the variable name to silence this warning.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol
index 8ebb46aa..31be70ca 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol
@@ -1,4 +1,3 @@
contract A { constructor(uint a) public { } }
contract B is A { }
// ----
-// Warning: (25-31): Unused function parameter. Remove or comment out the variable name to silence this warning.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol
index 9251df73..4f89e69e 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol
@@ -5,4 +5,3 @@ contract d {
function g() public { c(0).f(); }
}
// ----
-// Warning: (17-39): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol
index 824543ef..af392402 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol
@@ -2,4 +2,3 @@ contract test {
function f(uint) public { }
}
// ----
-// Warning: (20-47): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol
index a2ffc6e1..b524cd97 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol
@@ -2,4 +2,3 @@ contract test {
function f() public returns (bool) { }
}
// ----
-// Warning: (20-58): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol
index 93e5f065..9d2951b8 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol
@@ -1,3 +1,9 @@
-contract test { function() external { int x = -3; int y = -4; x ** y; } }
+contract test {
+ function f() public { int x = 3; int y = 4; x ** y; }
+ function g() public { int16 x = 3; uint8 y = 4; x ** y; }
+ function h() public { uint8 x = 3; int16 y = 4; x ** y; }
+}
// ----
-// TypeError: (62-68): Operator ** not compatible with types int256 and int256
+// TypeError: (64-70): Operator ** not compatible with types int256 and int256. Exponentiation is not allowed for signed integer types.
+// TypeError: (126-132): Operator ** not compatible with types int16 and uint8. Exponentiation is not allowed for signed integer types.
+// TypeError: (188-194): Operator ** not compatible with types uint8 and int16. Exponentiation is not allowed for signed integer types.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol
index c5175a41..0f4388d0 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol
@@ -6,5 +6,3 @@ contract C {
}
}
// ----
-// Warning: (91-106): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (80-122): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol
index 155281f5..f033b85b 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol
@@ -11,4 +11,3 @@ contract C {
}
}
// ----
-// Warning: (128-189): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol
index 20d8afa5..ee5bcda9 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol
@@ -5,4 +5,3 @@ library c {
}
// ----
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
-// Warning: (75-116): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol
index e9ab08ba..a92c07f3 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol
@@ -85,7 +85,5 @@ contract C {
}
// ----
// Warning: (1005-1019): This declaration shadows an existing declaration.
-// Warning: (90-116): Function state mutability can be restricted to pure
-// Warning: (121-147): Function state mutability can be restricted to pure
// Warning: (257-642): Function state mutability can be restricted to pure
// Warning: (647-1227): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol
index 441cf81e..295cf4ea 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol
@@ -10,4 +10,5 @@ contract test {
}
}
// ----
-// Warning: (20-707): Function state mutability can be restricted to pure
+// TypeError: (153-250): Type rational_const 9208...(70 digits omitted)...7637 / 1000...(71 digits omitted)...0000 is not implicitly convertible to expected type ufixed256x77, but it can be explicitly converted.
+// TypeError: (470-566): Type rational_const -933...(70 digits omitted)...0123 / 1000...(70 digits omitted)...0000 is not implicitly convertible to expected type fixed256x76, but it can be explicitly converted.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol
index 7f640505..109e8dfb 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol
@@ -5,4 +5,3 @@ contract test {
}
}
// ----
-// Warning: (20-57): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol
index 0032f99e..87a7b28d 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol
@@ -10,4 +10,3 @@ contract test {
}
}
// ----
-// Warning: (122-151): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol
index 2575954e..b4088068 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol
@@ -7,4 +7,3 @@ contract C {
}
// ----
// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
-// Warning: (112-164): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol
index 8a36eaad..4227cbb9 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol
@@ -3,4 +3,3 @@ contract C {
}
}
// ----
-// Warning: (28-34): Unused function parameter. Remove or comment out the variable name to silence this warning.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol
index b1422c4f..b4c9be35 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol
@@ -3,4 +3,3 @@ contract C {
}
}
// ----
-// Warning: (51-57): Unused function parameter. Remove or comment out the variable name to silence this warning.
diff --git a/test/libsolidity/syntaxTests/parsing/empty_function.sol b/test/libsolidity/syntaxTests/parsing/empty_function.sol
index 320a0bcc..3f42e4e3 100644
--- a/test/libsolidity/syntaxTests/parsing/empty_function.sol
+++ b/test/libsolidity/syntaxTests/parsing/empty_function.sol
@@ -3,7 +3,3 @@ contract test {
function functionName(bytes20 arg1, address addr) public view returns (int id) { }
}
// ----
-// Warning: (58-70): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (72-84): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (107-113): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (36-118): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/external_function.sol b/test/libsolidity/syntaxTests/parsing/external_function.sol
index 3aa3ceec..0315b200 100644
--- a/test/libsolidity/syntaxTests/parsing/external_function.sol
+++ b/test/libsolidity/syntaxTests/parsing/external_function.sol
@@ -2,4 +2,3 @@ contract c {
function x() external {}
}
// ----
-// Warning: (17-41): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol b/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol
index 94e1e60a..231be9c2 100644
--- a/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol
+++ b/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol
@@ -4,6 +4,3 @@ contract test {
function functionName(bytes32 input) public returns (bytes32 out) {}
}
// ----
-// Warning: (97-110): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (128-139): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (75-143): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol
index 11e77f25..42d8717a 100644
--- a/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol
+++ b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol
@@ -3,7 +3,3 @@ contract test {
function (uint, uint) internal returns (uint) f1 = f;
}
// ----
-// Warning: (31-37): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (39-45): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (63-69): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (20-73): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol b/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol
index 3defb5ea..000c2011 100644
--- a/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol
+++ b/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol
@@ -5,9 +5,5 @@ contract test {
}
}
// ----
-// Warning: (31-37): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (39-45): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (63-69): Unused function parameter. Remove or comment out the variable name to silence this warning.
// Warning: (108-156): Unused local variable.
-// Warning: (20-73): Function state mutability can be restricted to pure
// Warning: (78-167): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/library_simple.sol b/test/libsolidity/syntaxTests/parsing/library_simple.sol
index 006ff307..c892e3ed 100644
--- a/test/libsolidity/syntaxTests/parsing/library_simple.sol
+++ b/test/libsolidity/syntaxTests/parsing/library_simple.sol
@@ -2,4 +2,3 @@ library Lib {
function f() public { }
}
// ----
-// Warning: (18-41): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol b/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol
index cf986efe..6fa007c7 100644
--- a/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol
+++ b/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol
@@ -4,4 +4,3 @@ contract c {
function f() public mod1(7) mod2 { }
}
// ----
-// Warning: (135-171): Function state mutability can be restricted to view
diff --git a/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol b/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol
index 85d9e6a8..ba090c53 100644
--- a/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol
+++ b/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol
@@ -10,15 +10,3 @@ contract test {
function functionName4(bytes32 input) public returns (bytes32 out) {}
}
// ----
-// Warning: (97-110): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (128-139): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (203-216): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (234-245): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (304-317): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (335-346): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (410-423): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (441-452): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (74-143): Function state mutability can be restricted to pure
-// Warning: (180-249): Function state mutability can be restricted to pure
-// Warning: (281-350): Function state mutability can be restricted to pure
-// Warning: (387-456): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/no_function_params.sol b/test/libsolidity/syntaxTests/parsing/no_function_params.sol
index 5a024bdb..1f7c85a3 100644
--- a/test/libsolidity/syntaxTests/parsing/no_function_params.sol
+++ b/test/libsolidity/syntaxTests/parsing/no_function_params.sol
@@ -3,4 +3,3 @@ contract test {
function functionName() public {}
}
// ----
-// Warning: (36-69): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/single_function_param.sol b/test/libsolidity/syntaxTests/parsing/single_function_param.sol
index 955f20f0..8dbac272 100644
--- a/test/libsolidity/syntaxTests/parsing/single_function_param.sol
+++ b/test/libsolidity/syntaxTests/parsing/single_function_param.sol
@@ -3,6 +3,3 @@ contract test {
function functionName(bytes32 input) public returns (bytes32 out) {}
}
// ----
-// Warning: (58-71): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (89-100): Unused function parameter. Remove or comment out the variable name to silence this warning.
-// Warning: (36-104): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol b/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol
index 5646c43b..f2542d90 100644
--- a/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol
+++ b/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol
@@ -6,3 +6,4 @@ contract test {
}
// ----
// SyntaxError: (70-72): Use of unary + is disallowed.
+// TypeError: (70-72): Unary operator + cannot be applied to type uint256
diff --git a/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol b/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol
index db890b37..24071388 100644
--- a/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol
+++ b/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol
@@ -9,6 +9,3 @@ contract c {
}
// ----
// Warning: (58-71): This declaration shadows an existing declaration.
-// Warning: (89-111): Function state mutability can be restricted to pure
-// Warning: (116-144): Function state mutability can be restricted to pure
-// Warning: (149-182): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol b/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol
new file mode 100644
index 00000000..f721f4a9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol
@@ -0,0 +1,15 @@
+contract C {
+ address a;
+ function f() public pure returns(bool) {
+ a = address(0) + address(0);
+ a = address(0) - address(0);
+ a = address(0) * address(0);
+ a = address(0) / address(0);
+ return address(0) == address(0);
+ }
+}
+// ----
+// TypeError: (85-108): Operator + not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them.
+// TypeError: (122-145): Operator - not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them.
+// TypeError: (159-182): Operator * not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them.
+// TypeError: (196-219): Operator / not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them.
diff --git a/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol
index 1b0b5f94..31226781 100644
--- a/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol
+++ b/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol
@@ -5,5 +5,5 @@ contract c {
}
}
// ----
-// 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): Operator / not compatible with types rational_const 1 / 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168. Precision of rational constants is limited to 4096 bits.
// 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_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol
index 058db2e9..80b23eff 100644
--- a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol
+++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol
@@ -19,23 +19,23 @@ contract c {
}
}
// ----
-// TypeError: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4
+// TypeError: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits.
// 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-148): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits.
+// TypeError: (116-153): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits.
// 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: (217-228): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000
// TypeError: (242-254): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000
// TypeError: (268-280): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000
// TypeError: (294-307): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000
-// TypeError: (321-332): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2
+// TypeError: (321-332): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2. Precision of rational constants is limited to 4096 bits.
// TypeError: (321-332): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
-// TypeError: (346-358): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2
+// TypeError: (346-358): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2. Precision of rational constants is limited to 4096 bits.
// TypeError: (346-358): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
-// TypeError: (372-384): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2
+// TypeError: (372-384): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2. Precision of rational constants is limited to 4096 bits.
// TypeError: (372-384): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256.
-// TypeError: (398-411): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2
+// TypeError: (398-411): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2. Precision of rational constants is limited to 4096 bits.
// TypeError: (398-411): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256.
// TypeError: (425-441): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000
// TypeError: (425-441): 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_literal_to_fixed_implicit.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_to_fixed_implicit.sol
new file mode 100644
index 00000000..ca50e03b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/types/rational_number_literal_to_fixed_implicit.sol
@@ -0,0 +1,15 @@
+contract C {
+ function literalToUFixed() public pure {
+ ufixed8x2 a = 0.10;
+ ufixed8x2 b = 0.00;
+ ufixed8x2 c = 2.55;
+ a; b; c;
+ }
+ function literalToFixed() public pure {
+ fixed8x1 a = 0.1;
+ fixed8x1 b = 12.7;
+ fixed8x1 c = -12.8;
+ a; b; c;
+ }
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol
index bbed94b5..d725b43a 100644
--- a/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol
+++ b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol
@@ -5,5 +5,5 @@ contract c {
}
}
// ----
-// 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): Operator * not compatible with types int_const 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168. Precision of rational constants is limited to 4096 bits.
// 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/types/too_small_negative_numbers.sol b/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol
index 66bd9a8e..f86ffcdd 100644
--- a/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol
+++ b/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol
@@ -2,3 +2,4 @@ contract C {
fixed8x80 a = -1e-100;
}
// ----
+// TypeError: (29-36): Type rational_const -1 / 1000...(93 digits omitted)...0000 is not implicitly convertible to expected type fixed8x80, but it can be explicitly converted.
diff --git a/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol
index 87719eb3..5ec6d06f 100644
--- a/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol
+++ b/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol
@@ -2,4 +2,3 @@ contract C {
function g() view public { }
}
// ----
-// Warning: (17-45): Function state mutability can be restricted to pure
diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp
index a247a169..a337fa8d 100644
--- a/test/libyul/Common.cpp
+++ b/test/libyul/Common.cpp
@@ -29,6 +29,7 @@
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmPrinter.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ErrorReporter.h>
@@ -40,9 +41,9 @@ using namespace langutil;
using namespace yul;
using namespace dev::solidity;
-void yul::test::printErrors(ErrorList const& _errors, Scanner const& _scanner)
+void yul::test::printErrors(ErrorList const& _errors)
{
- SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; });
+ SourceReferenceFormatter formatter(cout);
for (auto const& error: _errors)
formatter.printExceptionInformation(
@@ -54,11 +55,11 @@ void yul::test::printErrors(ErrorList const& _errors, Scanner const& _scanner)
pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(string const& _source, bool _yul)
{
- auto flavour = _yul ? yul::AsmFlavour::Yul : yul::AsmFlavour::Strict;
+ shared_ptr<Dialect> dialect = _yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM();
ErrorList errors;
ErrorReporter errorReporter(errors);
auto scanner = make_shared<Scanner>(CharStream(_source, ""));
- auto parserResult = yul::Parser(errorReporter, flavour).parse(scanner, false);
+ auto parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false);
if (parserResult)
{
BOOST_REQUIRE(errorReporter.errors().empty());
@@ -68,7 +69,7 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(strin
errorReporter,
dev::test::Options::get().evmVersion(),
boost::none,
- flavour
+ dialect
);
if (analyzer.analyze(*parserResult))
{
@@ -76,7 +77,7 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(strin
return make_pair(parserResult, analysisInfo);
}
}
- printErrors(errors, *scanner);
+ printErrors(errors);
BOOST_FAIL("Invalid source.");
// Unreachable.
diff --git a/test/libyul/Common.h b/test/libyul/Common.h
index a1c64ca5..01fc416a 100644
--- a/test/libyul/Common.h
+++ b/test/libyul/Common.h
@@ -29,7 +29,6 @@
namespace langutil
{
-class Scanner;
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
}
@@ -44,7 +43,7 @@ namespace yul
namespace test
{
-void printErrors(langutil::ErrorList const& _errors, langutil::Scanner const& _scanner);
+void printErrors(langutil::ErrorList const& _errors);
std::pair<std::shared_ptr<Block>, std::shared_ptr<AsmAnalysisInfo>>
parse(std::string const& _source, bool _yul = true);
Block disambiguate(std::string const& _source, bool _yul = true);
diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp
new file mode 100644
index 00000000..e60f718d
--- /dev/null
+++ b/test/libyul/ObjectCompilerTest.cpp
@@ -0,0 +1,134 @@
+/*
+ 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/libyul/ObjectCompilerTest.h>
+
+#include <test/libsolidity/FormattedScope.h>
+
+#include <libsolidity/interface/AssemblyStack.h>
+
+#include <libevmasm/Instruction.h>
+
+#include <liblangutil/SourceReferenceFormatter.h>
+
+#include <boost/algorithm/string.hpp>
+
+#include <fstream>
+
+using namespace dev;
+using namespace langutil;
+using namespace yul;
+using namespace yul::test;
+using namespace dev::solidity;
+using namespace dev::solidity::test;
+using namespace std;
+
+ObjectCompilerTest::ObjectCompilerTest(string const& _filename)
+{
+ boost::filesystem::path path(_filename);
+
+ ifstream file(_filename);
+ if (!file)
+ BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\"."));
+ file.exceptions(ios::badbit);
+
+ string line;
+ while (getline(file, line))
+ {
+ if (boost::algorithm::starts_with(line, "// ----"))
+ break;
+ if (m_source.empty() && boost::algorithm::starts_with(line, "// optimize"))
+ m_optimize = true;
+ m_source += line + "\n";
+ }
+ while (getline(file, line))
+ if (boost::algorithm::starts_with(line, "//"))
+ m_expectation += line.substr((line.size() >= 3 && line[2] == ' ') ? 3 : 2) + "\n";
+ else
+ m_expectation += line + "\n";
+}
+
+bool ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
+{
+ AssemblyStack stack(EVMVersion(), AssemblyStack::Language::StrictAssembly);
+ if (!stack.parseAndAnalyze("source", m_source))
+ {
+ FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
+ printErrors(_stream, stack.errors());
+ return false;
+ }
+ if (m_optimize)
+ stack.optimize();
+
+ MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM);
+ solAssert(obj.bytecode, "");
+
+ m_obtainedResult = "Assembly:\n" + obj.assembly;
+ if (obj.bytecode->bytecode.empty())
+ m_obtainedResult += "-- empty bytecode --\n";
+ else
+ m_obtainedResult +=
+ "Bytecode: " +
+ toHex(obj.bytecode->bytecode) +
+ "\nOpcodes: " +
+ boost::trim_copy(solidity::disassemble(obj.bytecode->bytecode)) +
+ "\n";
+
+ if (m_expectation != m_obtainedResult)
+ {
+ string nextIndentLevel = _linePrefix + " ";
+ FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Expected result:" << endl;
+ printIndented(_stream, m_expectation, nextIndentLevel);
+ FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Obtained result:" << endl;
+ printIndented(_stream, m_obtainedResult, nextIndentLevel);
+ return false;
+ }
+ return true;
+}
+
+void ObjectCompilerTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const
+{
+ printIndented(_stream, m_source, _linePrefix);
+}
+
+void ObjectCompilerTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const
+{
+ printIndented(_stream, m_obtainedResult, _linePrefix);
+}
+
+void ObjectCompilerTest::printIndented(ostream& _stream, string const& _output, string const& _linePrefix) const
+{
+ stringstream output(_output);
+ string line;
+ while (getline(output, line))
+ if (line.empty())
+ // Avoid trailing spaces.
+ _stream << boost::trim_right_copy(_linePrefix) << endl;
+ else
+ _stream << _linePrefix << line << endl;
+}
+
+void ObjectCompilerTest::printErrors(ostream& _stream, ErrorList const& _errors)
+{
+ SourceReferenceFormatter formatter(_stream);
+
+ for (auto const& error: _errors)
+ formatter.printExceptionInformation(
+ *error,
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error"
+ );
+}
diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h
new file mode 100644
index 00000000..a5f8d777
--- /dev/null
+++ b/test/libyul/ObjectCompilerTest.h
@@ -0,0 +1,69 @@
+/*
+ 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/TestCase.h>
+
+namespace langutil
+{
+class Scanner;
+class Error;
+using ErrorList = std::vector<std::shared_ptr<Error const>>;
+}
+
+namespace yul
+{
+struct AsmAnalysisInfo;
+struct Block;
+}
+
+namespace yul
+{
+namespace test
+{
+
+class ObjectCompilerTest: public dev::solidity::test::TestCase
+{
+public:
+ static std::unique_ptr<TestCase> create(std::string const& _filename)
+ {
+ return std::unique_ptr<TestCase>(new ObjectCompilerTest(_filename));
+ }
+
+ explicit ObjectCompilerTest(std::string const& _filename);
+
+ bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override;
+
+ void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override;
+ void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override;
+
+private:
+ void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const;
+ bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
+ void disambiguate();
+
+ static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
+
+ std::string m_source;
+ bool m_optimize = false;
+ std::string m_expectation;
+ std::string m_obtainedResult;
+};
+
+}
+}
diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp
index caaf2719..df7e32a1 100644
--- a/test/libyul/Parser.cpp
+++ b/test/libyul/Parser.cpp
@@ -22,10 +22,12 @@
#include <test/Options.h>
#include <test/libsolidity/ErrorCheck.h>
+#include <test/libyul/Common.h>
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
+#include <libyul/Dialect.h>
#include <liblangutil/Scanner.h>
#include <liblangutil/ErrorReporter.h>
@@ -47,12 +49,12 @@ namespace test
namespace
{
-bool parse(string const& _source, ErrorReporter& errorReporter)
+bool parse(string const& _source, std::shared_ptr<Dialect> _dialect, ErrorReporter& errorReporter)
{
try
{
auto scanner = make_shared<Scanner>(CharStream(_source, ""));
- auto parserResult = yul::Parser(errorReporter, yul::AsmFlavour::Yul).parse(scanner, false);
+ auto parserResult = yul::Parser(errorReporter, _dialect).parse(scanner, false);
if (parserResult)
{
yul::AsmAnalysisInfo analysisInfo;
@@ -61,7 +63,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
errorReporter,
dev::test::Options::get().evmVersion(),
boost::none,
- yul::AsmFlavour::Yul
+ _dialect
)).analyze(*parserResult);
}
}
@@ -72,13 +74,14 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
return false;
}
-boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true)
+boost::optional<Error> parseAndReturnFirstError(string const& _source, shared_ptr<Dialect> _dialect, bool _allowWarnings = true)
{
ErrorList errors;
ErrorReporter errorReporter(errors);
- if (!parse(_source, errorReporter))
+ if (!parse(_source, _dialect, errorReporter))
{
- BOOST_REQUIRE_EQUAL(errors.size(), 1);
+ BOOST_REQUIRE(!errors.empty());
+ BOOST_CHECK_EQUAL(errors.size(), 1);
return *errors.front();
}
else
@@ -96,29 +99,31 @@ boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _all
return {};
}
-bool successParse(std::string const& _source, bool _allowWarnings = true)
+bool successParse(std::string const& _source, shared_ptr<Dialect> _dialect = Dialect::yul(), bool _allowWarnings = true)
{
- return !parseAndReturnFirstError(_source, _allowWarnings);
+ return !parseAndReturnFirstError(_source, _dialect, _allowWarnings);
}
-Error expectError(std::string const& _source, bool _allowWarnings = false)
+Error expectError(std::string const& _source, shared_ptr<Dialect> _dialect = Dialect::yul(), bool _allowWarnings = false)
{
- auto error = parseAndReturnFirstError(_source, _allowWarnings);
+ auto error = parseAndReturnFirstError(_source, _dialect, _allowWarnings);
BOOST_REQUIRE(error);
return *error;
}
}
-#define CHECK_ERROR(text, typ, substring) \
+#define CHECK_ERROR_DIALECT(text, typ, substring, dialect) \
do \
{ \
- Error err = expectError((text), false); \
+ Error err = expectError((text), dialect, false); \
BOOST_CHECK(err.type() == (Error::Type::typ)); \
BOOST_CHECK(dev::solidity::searchErrorMessage(err, (substring))); \
} while(0)
+#define CHECK_ERROR(text, typ, substring) CHECK_ERROR_DIALECT(text, typ, substring, Dialect::yul())
+
BOOST_AUTO_TEST_SUITE(YulParser)
BOOST_AUTO_TEST_CASE(smoke_test)
@@ -299,6 +304,42 @@ BOOST_AUTO_TEST_CASE(if_statement_invalid)
BOOST_CHECK(successParse("{ if 42:u256 { } }"));
}
+BOOST_AUTO_TEST_CASE(builtins_parser)
+{
+ struct SimpleDialect: public Dialect
+ {
+ SimpleDialect(): Dialect(AsmFlavour::Strict) {}
+ BuiltinFunction const* builtin(YulString _name) const override
+ {
+ return _name == "builtin"_yulstring ? &f : nullptr;
+ }
+ BuiltinFunction f;
+ };
+
+ shared_ptr<Dialect> dialect = make_shared<SimpleDialect>();
+ CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
+ CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
+ CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
+}
+
+BOOST_AUTO_TEST_CASE(builtins_analysis)
+{
+ struct SimpleDialect: public Dialect
+ {
+ SimpleDialect(): Dialect(AsmFlavour::Strict) {}
+ BuiltinFunction const* builtin(YulString _name) const override
+ {
+ return _name == "builtin"_yulstring ? &f : nullptr;
+ }
+ BuiltinFunction f{"builtin"_yulstring, vector<Type>(2), vector<Type>(3), false};
+ };
+
+ shared_ptr<Dialect> dialect = make_shared<SimpleDialect>();
+ BOOST_CHECK(successParse("{ let a, b, c := builtin(1, 2) }", dialect));
+ CHECK_ERROR_DIALECT("{ let a, b, c := builtin(1) }", TypeError, "Function expects 2 arguments but got 1", dialect);
+ CHECK_ERROR_DIALECT("{ let a, b := builtin(1, 2) }", DeclarationError, "Variable count mismatch: 2 variables and 3 values.", dialect);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libyul/StackReuseCodegen.cpp b/test/libyul/StackReuseCodegen.cpp
new file mode 100644
index 00000000..97be11d3
--- /dev/null
+++ b/test/libyul/StackReuseCodegen.cpp
@@ -0,0 +1,353 @@
+/*
+ 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 stack-reusing code generator.
+ */
+
+#include <test/Options.h>
+
+#include <libsolidity/interface/AssemblyStack.h>
+#include <libevmasm/Instruction.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace yul
+{
+namespace test
+{
+
+namespace
+{
+string assemble(string const& _input)
+{
+ solidity::AssemblyStack asmStack;
+ BOOST_REQUIRE_MESSAGE(asmStack.parseAndAnalyze("", _input), "Source did not parse: " + _input);
+ return solidity::disassemble(asmStack.assemble(solidity::AssemblyStack::Machine::EVM, true).bytecode->bytecode);
+}
+}
+
+BOOST_AUTO_TEST_SUITE(StackReuseCodegen)
+
+BOOST_AUTO_TEST_CASE(smoke_test)
+{
+ string out = assemble("{}");
+ BOOST_CHECK_EQUAL(out, "");
+}
+
+BOOST_AUTO_TEST_CASE(single_var)
+{
+ string out = assemble("{ let x }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x0 POP ");
+}
+
+BOOST_AUTO_TEST_CASE(single_var_assigned)
+{
+ string out = assemble("{ let x := 1 }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x1 POP ");
+}
+
+BOOST_AUTO_TEST_CASE(single_var_assigned_plus_code)
+{
+ string out = assemble("{ let x := 1 mstore(3, 4) }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x1 POP PUSH1 0x4 PUSH1 0x3 MSTORE ");
+}
+
+BOOST_AUTO_TEST_CASE(single_var_assigned_plus_code_and_reused)
+{
+ string out = assemble("{ let x := 1 mstore(3, 4) pop(mload(x)) }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x1 PUSH1 0x4 PUSH1 0x3 MSTORE DUP1 MLOAD POP POP ");
+}
+
+BOOST_AUTO_TEST_CASE(multi_reuse_single_slot)
+{
+ string out = assemble("{ let x := 1 x := 6 let y := 2 y := 4 }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x1 PUSH1 0x6 SWAP1 POP POP PUSH1 0x2 PUSH1 0x4 SWAP1 POP POP ");
+}
+
+BOOST_AUTO_TEST_CASE(multi_reuse_single_slot_nested)
+{
+ string out = assemble("{ let x := 1 x := 6 { let y := 2 y := 4 } }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x1 PUSH1 0x6 SWAP1 POP POP PUSH1 0x2 PUSH1 0x4 SWAP1 POP POP ");
+}
+
+BOOST_AUTO_TEST_CASE(multi_reuse_same_variable_name)
+{
+ string out = assemble("{ let z := mload(0) { let x := 1 x := 6 z := x } { let x := 2 z := x x := 4 } }");
+ BOOST_CHECK_EQUAL(out,
+ "PUSH1 0x0 MLOAD "
+ "PUSH1 0x1 PUSH1 0x6 SWAP1 POP DUP1 SWAP2 POP POP "
+ "PUSH1 0x2 DUP1 SWAP2 POP PUSH1 0x4 SWAP1 POP POP "
+ "POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(last_use_in_nested_block)
+{
+ string out = assemble("{ let z := 0 { pop(z) } let x := 1 }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x0 DUP1 POP POP PUSH1 0x1 POP ");
+}
+
+BOOST_AUTO_TEST_CASE(if_)
+{
+ // z is only removed after the if (after the jumpdest)
+ string out = assemble("{ let z := mload(0) if z { let x := z } let t := 3 }");
+ BOOST_CHECK_EQUAL(out, "PUSH1 0x0 MLOAD DUP1 ISZERO PUSH1 0xA JUMPI DUP1 POP JUMPDEST POP PUSH1 0x3 POP ");
+}
+
+BOOST_AUTO_TEST_CASE(switch_)
+{
+ string out = assemble("{ let z := 0 switch z case 0 { let x := 2 let y := 3 } default { z := 3 } let t := 9 }");
+ BOOST_CHECK_EQUAL(out,
+ "PUSH1 0x0 DUP1 "
+ "PUSH1 0x0 DUP2 EQ PUSH1 0x11 JUMPI "
+ "PUSH1 0x3 SWAP2 POP PUSH1 0x18 JUMP "
+ "JUMPDEST PUSH1 0x2 POP PUSH1 0x3 POP "
+ "JUMPDEST POP POP " // This is where z and its copy (switch condition) can be removed.
+ "PUSH1 0x9 POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(reuse_slots)
+{
+ // x and y should reuse the slots of b and d
+ string out = assemble("{ let a, b, c, d let x := 2 let y := 3 mstore(x, a) mstore(y, c) }");
+ BOOST_CHECK_EQUAL(out,
+ "PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 "
+ "POP " // d is removed right away
+ "PUSH1 0x2 SWAP2 POP " // x is stored at b's slot
+ "PUSH1 0x3 DUP4 DUP4 MSTORE "
+ "DUP2 DUP2 MSTORE "
+ "POP POP POP POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(for_1)
+{
+ // Special scoping rules, but can remove z early
+ string out = assemble("{ for { let z := 0 } 1 { } { let x := 3 } let t := 2 }");
+ BOOST_CHECK_EQUAL(out,
+ "PUSH1 0x0 POP "
+ "JUMPDEST PUSH1 0x1 ISZERO PUSH1 0x11 JUMPI "
+ "PUSH1 0x3 POP JUMPDEST PUSH1 0x3 JUMP "
+ "JUMPDEST PUSH1 0x2 POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(for_2)
+{
+ // Special scoping rules, cannot remove z until after the loop!
+ string out = assemble("{ for { let z := 0 } 1 { } { z := 8 let x := 3 } let t := 2 }");
+ BOOST_CHECK_EQUAL(out,
+ "PUSH1 0x0 "
+ "JUMPDEST PUSH1 0x1 ISZERO PUSH1 0x14 JUMPI "
+ "PUSH1 0x8 SWAP1 POP "
+ "PUSH1 0x3 POP "
+ "JUMPDEST PUSH1 0x2 JUMP "
+ "JUMPDEST POP " // z is removed
+ "PUSH1 0x2 POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_trivial)
+{
+ string in = R"({
+ function f() { }
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x5 JUMP JUMPDEST JUMP JUMPDEST "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_retparam)
+{
+ string in = R"({
+ function f() -> x, y { }
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0xB JUMP "
+ "JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 JUMP "
+ "JUMPDEST "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_params)
+{
+ string in = R"({
+ function f(a, b) { }
+ })";
+ BOOST_CHECK_EQUAL(assemble(in), "PUSH1 0x7 JUMP JUMPDEST POP POP JUMP JUMPDEST ");
+}
+
+BOOST_AUTO_TEST_CASE(function_params_and_retparams)
+{
+ string in = R"({
+ function f(a, b, c, d) -> x, y { }
+ })";
+ // This does not re-use the parameters for the return parameters
+ // We do not expect parameters to be fully unused, so the stack
+ // layout for a function is still fixed, even though parameters
+ // can be re-used.
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x10 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP JUMPDEST "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_params_and_retparams_partly_unused)
+{
+ string in = R"({
+ function f(a, b, c, d) -> x, y { b := 3 let s := 9 y := 2 mstore(s, y) }
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x1E JUMP "
+ "JUMPDEST PUSH1 0x0 PUSH1 0x0 "
+ "PUSH1 0x3 SWAP4 POP "
+ "PUSH1 0x9 PUSH1 0x2 SWAP2 POP "
+ "DUP2 DUP2 MSTORE "
+ "POP SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP "
+ "JUMPDEST "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_with_body_embedded)
+{
+ string in = R"({
+ let b := 3
+ function f(a, r) -> t {
+ // r could be removed right away, but a cannot - this is not implemented, though
+ let x := a a := 3 t := a
+ }
+ b := 7
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x3 PUSH1 "
+ "0x16 JUMP "
+ "JUMPDEST PUSH1 0x0 " // start of f, initialize t
+ "DUP2 POP " // let x := a
+ "PUSH1 0x3 SWAP2 POP "
+ "DUP2 SWAP1 POP "
+ "SWAP3 SWAP2 POP POP JUMP "
+ "JUMPDEST PUSH1 0x7 SWAP1 "
+ "POP POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(function_call)
+{
+ string in = R"({
+ let b := f(1, 2)
+ function f(a, r) -> t { }
+ b := f(3, 4)
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x9 PUSH1 0x2 PUSH1 0x1 PUSH1 0xD JUMP "
+ "JUMPDEST PUSH1 0x15 JUMP " // jump over f
+ "JUMPDEST PUSH1 0x0 SWAP3 SWAP2 POP POP JUMP " // f
+ "JUMPDEST PUSH1 0x1F PUSH1 0x4 PUSH1 0x3 PUSH1 0xD JUMP "
+ "JUMPDEST SWAP1 POP POP "
+ );
+}
+
+
+BOOST_AUTO_TEST_CASE(functions_multi_return)
+{
+ string in = R"({
+ function f(a, b) -> t { }
+ function g() -> r, s { }
+ let x := f(1, 2)
+ x := f(3, 4)
+ let y, z := g()
+ y, z := g()
+ let unused := 7
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0xB JUMP "
+ "JUMPDEST PUSH1 0x0 SWAP3 SWAP2 POP POP JUMP " // f
+ "JUMPDEST PUSH1 0x17 JUMP "
+ "JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 JUMP " // g
+ "JUMPDEST PUSH1 0x21 PUSH1 0x2 PUSH1 0x1 PUSH1 0x3 JUMP " // f(1, 2)
+ "JUMPDEST PUSH1 0x2B PUSH1 0x4 PUSH1 0x3 PUSH1 0x3 JUMP " // f(3, 4)
+ "JUMPDEST SWAP1 POP " // assignment to x
+ "POP " // remove x
+ "PUSH1 0x34 PUSH1 0xF JUMP " // g()
+ "JUMPDEST PUSH1 0x3A PUSH1 0xF JUMP " // g()
+ "JUMPDEST SWAP2 POP SWAP2 POP " // assignments
+ "POP POP " // removal of y and z
+ "PUSH1 0x7 POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(reuse_slots_function)
+{
+ string in = R"({
+ function f() -> x, y, z, t {}
+ let a, b, c, d := f() let x1 := 2 let y1 := 3 mstore(x1, a) mstore(y1, c)
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x11 JUMP "
+ "JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 SWAP3 SWAP4 JUMP "
+ "JUMPDEST PUSH1 0x17 PUSH1 0x3 JUMP "
+ // Stack: a b c d
+ "JUMPDEST POP " // d is unused
+ // Stack: a b c
+ "PUSH1 0x2 SWAP2 POP " // x1 reuses b's slot
+ "PUSH1 0x3 "
+ // Stack: a x1 c y1
+ "DUP4 DUP4 MSTORE "
+ "DUP2 DUP2 MSTORE "
+ "POP POP POP POP "
+ );
+}
+
+BOOST_AUTO_TEST_CASE(reuse_slots_function_with_gaps)
+{
+ string in = R"({
+ // Only x3 is actually used, the slots of
+ // x1 and x2 will be reused right away.
+ let x1 := 5 let x2 := 6 let x3 := 7
+ mstore(x1, x2)
+ function f() -> x, y, z, t {}
+ let a, b, c, d := f() mstore(x3, a) mstore(c, d)
+ })";
+ BOOST_CHECK_EQUAL(assemble(in),
+ "PUSH1 0x5 PUSH1 0x6 PUSH1 0x7 "
+ "DUP2 DUP4 MSTORE "
+ "PUSH1 0x1A JUMP " // jump across function
+ "JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 SWAP3 SWAP4 JUMP "
+ "JUMPDEST PUSH1 0x20 PUSH1 0xC JUMP "
+ // stack: x1 x2 x3 a b c d
+ "JUMPDEST SWAP6 POP " // move d into x1
+ // stack: d x2 x3 a b c
+ "SWAP4 POP "
+ // stack: d c x3 a b
+ "POP "
+ // stack: d c x3 a
+ "DUP1 DUP3 MSTORE "
+ "POP POP "
+ // stack: d c
+ "DUP2 DUP2 MSTORE "
+ "POP POP "
+ );
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+}
diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp
index 15d70faa..9643a1e9 100644
--- a/test/libyul/YulOptimizerTest.cpp
+++ b/test/libyul/YulOptimizerTest.cpp
@@ -22,7 +22,7 @@
#include <test/Options.h>
#include <libyul/optimiser/BlockFlattener.h>
-#include <libyul/optimiser/VarDeclPropagator.h>
+#include <libyul/optimiser/VarDeclInitializer.h>
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
#include <libyul/optimiser/NameCollector.h>
@@ -39,7 +39,9 @@
#include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/SSATransform.h>
#include <libyul/optimiser/RedundantAssignEliminator.h>
+#include <libyul/optimiser/StructuralSimplifier.h>
#include <libyul/optimiser/Suite.h>
+#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AsmPrinter.h>
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
@@ -105,11 +107,8 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con
disambiguate();
BlockFlattener{}(*m_ast);
}
- else if (m_optimizerStep == "varDeclPropagator")
- {
- disambiguate();
- VarDeclPropagator{}(*m_ast);
- }
+ else if (m_optimizerStep == "varDeclInitializer")
+ VarDeclInitializer{}(*m_ast);
else if (m_optimizerStep == "forLoopInitRewriter")
{
disambiguate();
@@ -213,6 +212,11 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con
SSATransform::run(*m_ast, nameDispenser);
RedundantAssignEliminator::run(*m_ast);
}
+ else if (m_optimizerStep == "structuralSimplifier")
+ {
+ disambiguate();
+ StructuralSimplifier{}(*m_ast);
+ }
else if (m_optimizerStep == "fullSuite")
OptimiserSuite::run(*m_ast, *m_analysisInfo);
else
@@ -256,15 +260,15 @@ void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, st
bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted)
{
- yul::AsmFlavour flavour = m_yul ? yul::AsmFlavour::Yul : yul::AsmFlavour::Strict;
+ shared_ptr<yul::Dialect> dialect = m_yul ? yul::Dialect::yul() : yul::EVMDialect::strictAssemblyForEVM();
ErrorList errors;
ErrorReporter errorReporter(errors);
shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(m_source, ""));
- m_ast = yul::Parser(errorReporter, flavour).parse(scanner, false);
+ m_ast = yul::Parser(errorReporter, dialect).parse(scanner, false);
if (!m_ast || !errorReporter.errors().empty())
{
FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
- printErrors(_stream, errorReporter.errors(), *scanner);
+ printErrors(_stream, errorReporter.errors());
return false;
}
m_analysisInfo = make_shared<yul::AsmAnalysisInfo>();
@@ -273,12 +277,12 @@ bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool c
errorReporter,
dev::test::Options::get().evmVersion(),
boost::none,
- flavour
+ dialect
);
if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty())
{
FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error analyzing source." << endl;
- printErrors(_stream, errorReporter.errors(), *scanner);
+ printErrors(_stream, errorReporter.errors());
return false;
}
return true;
@@ -290,9 +294,9 @@ void YulOptimizerTest::disambiguate()
m_analysisInfo.reset();
}
-void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors, Scanner const& _scanner)
+void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors)
{
- SourceReferenceFormatter formatter(_stream, [&](string const&) -> Scanner const& { return _scanner; });
+ SourceReferenceFormatter formatter(_stream);
for (auto const& error: _errors)
formatter.printExceptionInformation(
diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h
index 90026e24..5648e995 100644
--- a/test/libyul/YulOptimizerTest.h
+++ b/test/libyul/YulOptimizerTest.h
@@ -57,7 +57,7 @@ private:
bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
void disambiguate();
- static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors, langutil::Scanner const& _scanner);
+ static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
std::string m_source;
bool m_yul = false;
diff --git a/test/libyul/objectCompiler/data.yul b/test/libyul/objectCompiler/data.yul
new file mode 100644
index 00000000..daa22d21
--- /dev/null
+++ b/test/libyul/objectCompiler/data.yul
@@ -0,0 +1,11 @@
+object "a" {
+ code {}
+ // Unreferenced data is not added to the assembled bytecode.
+ data "str" "Hello, World!"
+}
+// ----
+// Assembly:
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// Bytecode: fe
+// Opcodes: INVALID
diff --git a/test/libyul/objectCompiler/datacopy.yul b/test/libyul/objectCompiler/datacopy.yul
new file mode 100644
index 00000000..2259e5dc
--- /dev/null
+++ b/test/libyul/objectCompiler/datacopy.yul
@@ -0,0 +1,48 @@
+object "a" {
+ code {
+ datacopy(0, dataoffset("sub"), datasize("sub"))
+ return(0, datasize("sub"))
+ }
+ object "sub" {
+ code {
+ sstore(0, dataoffset("sub"))
+ mstore(0, datasize("data1"))
+ }
+ data "data1" "Hello, World!"
+ }
+}
+// ----
+// Assembly:
+// /* "source":26:73 */
+// dataSize(sub_0)
+// dataOffset(sub_0)
+// /* "source":35:36 */
+// 0x00
+// /* "source":26:73 */
+// codecopy
+// /* "source":78:104 */
+// dataSize(sub_0)
+// /* "source":85:86 */
+// 0x00
+// /* "source":78:104 */
+// return
+// stop
+//
+// sub_0: assembly {
+// /* "source":143:171 */
+// 0x00
+// /* "source":150:151 */
+// 0x00
+// /* "source":143:171 */
+// sstore
+// /* "source":178:206 */
+// 0x0d
+// /* "source":185:186 */
+// 0x00
+// /* "source":178:206 */
+// mstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// }
+// Bytecode: 600b600d600039600b6000f3fe6000600055600d600052fe
+// Opcodes: PUSH1 0xB PUSH1 0xD PUSH1 0x0 CODECOPY PUSH1 0xB PUSH1 0x0 RETURN INVALID PUSH1 0x0 PUSH1 0x0 SSTORE PUSH1 0xD PUSH1 0x0 MSTORE INVALID
diff --git a/test/libyul/objectCompiler/dataoffset_code.yul b/test/libyul/objectCompiler/dataoffset_code.yul
new file mode 100644
index 00000000..725267f2
--- /dev/null
+++ b/test/libyul/objectCompiler/dataoffset_code.yul
@@ -0,0 +1,29 @@
+object "a" {
+ code { sstore(0, dataoffset("sub")) }
+ object "sub" {
+ code { sstore(0, 8) }
+ data "data1" "Hello, World!"
+ }
+}
+// ----
+// Assembly:
+// /* "source":22:50 */
+// dataOffset(sub_0)
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:50 */
+// sstore
+// stop
+//
+// sub_0: assembly {
+// /* "source":91:92 */
+// 0x08
+// /* "source":88:89 */
+// 0x00
+// /* "source":81:93 */
+// sstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// }
+// Bytecode: 6006600055fe6008600055fe
+// Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID PUSH1 0x8 PUSH1 0x0 SSTORE INVALID
diff --git a/test/libyul/objectCompiler/dataoffset_data.yul b/test/libyul/objectCompiler/dataoffset_data.yul
new file mode 100644
index 00000000..9a0a461d
--- /dev/null
+++ b/test/libyul/objectCompiler/dataoffset_data.yul
@@ -0,0 +1,16 @@
+object "a" {
+ code { sstore(0, dataoffset("data1")) }
+ data "data1" "Hello, World!"
+}
+// ----
+// Assembly:
+// /* "source":22:52 */
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:52 */
+// sstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// Bytecode: 6006600055fe48656c6c6f2c20576f726c6421
+// Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID 0x48 PUSH6 0x6C6C6F2C2057 PUSH16 0x726C6421000000000000000000000000
diff --git a/test/libyul/objectCompiler/dataoffset_self.yul b/test/libyul/objectCompiler/dataoffset_self.yul
new file mode 100644
index 00000000..b7740735
--- /dev/null
+++ b/test/libyul/objectCompiler/dataoffset_self.yul
@@ -0,0 +1,16 @@
+object "a" {
+ code { sstore(0, dataoffset("a")) }
+ data "data1" "Hello, World!"
+}
+// ----
+// Assembly:
+// /* "source":22:48 */
+// 0x00
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:48 */
+// sstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// Bytecode: 6000600055fe
+// Opcodes: PUSH1 0x0 PUSH1 0x0 SSTORE INVALID
diff --git a/test/libyul/objectCompiler/datasize_code.yul b/test/libyul/objectCompiler/datasize_code.yul
new file mode 100644
index 00000000..cff68515
--- /dev/null
+++ b/test/libyul/objectCompiler/datasize_code.yul
@@ -0,0 +1,29 @@
+object "a" {
+ code { sstore(0, datasize("sub")) }
+ object "sub" {
+ code { sstore(0, 8) }
+ data "data1" "Hello, World!"
+ }
+}
+// ----
+// Assembly:
+// /* "source":22:48 */
+// dataSize(sub_0)
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:48 */
+// sstore
+// stop
+//
+// sub_0: assembly {
+// /* "source":89:90 */
+// 0x08
+// /* "source":86:87 */
+// 0x00
+// /* "source":79:91 */
+// sstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// }
+// Bytecode: 6006600055fe
+// Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID
diff --git a/test/libyul/objectCompiler/datasize_data.yul b/test/libyul/objectCompiler/datasize_data.yul
new file mode 100644
index 00000000..f8341469
--- /dev/null
+++ b/test/libyul/objectCompiler/datasize_data.yul
@@ -0,0 +1,16 @@
+object "a" {
+ code { sstore(0, datasize("data1")) }
+ data "data1" "Hello, World!"
+}
+// ----
+// Assembly:
+// /* "source":22:50 */
+// 0x0d
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:50 */
+// sstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// Bytecode: 600d600055fe
+// Opcodes: PUSH1 0xD PUSH1 0x0 SSTORE INVALID
diff --git a/test/libyul/objectCompiler/datasize_self.yul b/test/libyul/objectCompiler/datasize_self.yul
new file mode 100644
index 00000000..4579fe67
--- /dev/null
+++ b/test/libyul/objectCompiler/datasize_self.yul
@@ -0,0 +1,16 @@
+object "a" {
+ code { sstore(0, datasize("a")) }
+ data "data1" "Hello, World!"
+}
+// ----
+// Assembly:
+// /* "source":22:46 */
+// bytecodeSize
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:46 */
+// sstore
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// Bytecode: 6006600055fe
+// Opcodes: PUSH1 0x6 PUSH1 0x0 SSTORE INVALID
diff --git a/test/libyul/objectCompiler/namedObject.yul b/test/libyul/objectCompiler/namedObject.yul
new file mode 100644
index 00000000..940160fd
--- /dev/null
+++ b/test/libyul/objectCompiler/namedObject.yul
@@ -0,0 +1,6 @@
+object "a" {
+ code {}
+}
+// ----
+// Assembly:
+// -- empty bytecode --
diff --git a/test/libyul/objectCompiler/namedObjectCode.yul b/test/libyul/objectCompiler/namedObjectCode.yul
new file mode 100644
index 00000000..4fc6891c
--- /dev/null
+++ b/test/libyul/objectCompiler/namedObjectCode.yul
@@ -0,0 +1,13 @@
+object "a" {
+ code { sstore(0, 1) }
+}
+// ----
+// Assembly:
+// /* "source":32:33 */
+// 0x01
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:34 */
+// sstore
+// Bytecode: 6001600055
+// Opcodes: PUSH1 0x1 PUSH1 0x0 SSTORE
diff --git a/test/libyul/objectCompiler/nested_optimizer.yul b/test/libyul/objectCompiler/nested_optimizer.yul
new file mode 100644
index 00000000..7739ce61
--- /dev/null
+++ b/test/libyul/objectCompiler/nested_optimizer.yul
@@ -0,0 +1,49 @@
+// optimize
+object "a" {
+ code {
+ let x := calldataload(0)
+ let y := calldataload(0)
+ let z := sub(y, x)
+ sstore(add(x, 0), z)
+ }
+ object "sub" {
+ code {
+ let x := calldataload(0)
+ let y := calldataload(0)
+ let z := sub(y, x)
+ sstore(add(x, 0), z)
+ }
+ }
+}
+// ----
+// Assembly:
+// /* "source":60:61 */
+// 0x00
+// /* "source":137:138 */
+// dup1
+// /* "source":60:61 */
+// dup2
+// /* "source":47:62 */
+// calldataload
+// /* "source":119:139 */
+// sstore
+// /* "source":32:143 */
+// pop
+// stop
+//
+// sub_0: assembly {
+// /* "source":200:201 */
+// 0x00
+// /* "source":283:284 */
+// dup1
+// /* "source":200:201 */
+// dup2
+// /* "source":187:202 */
+// calldataload
+// /* "source":265:285 */
+// sstore
+// /* "source":170:291 */
+// pop
+// }
+// Bytecode: 60008081355550fe
+// Opcodes: PUSH1 0x0 DUP1 DUP2 CALLDATALOAD SSTORE POP INVALID
diff --git a/test/libyul/objectCompiler/simple.yul b/test/libyul/objectCompiler/simple.yul
new file mode 100644
index 00000000..d41b527c
--- /dev/null
+++ b/test/libyul/objectCompiler/simple.yul
@@ -0,0 +1,13 @@
+{
+ sstore(0, 1)
+}
+// ----
+// Assembly:
+// /* "source":14:15 */
+// 0x01
+// /* "source":11:12 */
+// 0x00
+// /* "source":4:16 */
+// sstore
+// Bytecode: 6001600055
+// Opcodes: PUSH1 0x1 PUSH1 0x0 SSTORE
diff --git a/test/libyul/objectCompiler/simple_optimizer.yul b/test/libyul/objectCompiler/simple_optimizer.yul
new file mode 100644
index 00000000..43b33553
--- /dev/null
+++ b/test/libyul/objectCompiler/simple_optimizer.yul
@@ -0,0 +1,23 @@
+// optimize
+{
+ let x := calldataload(0)
+ let y := calldataload(0)
+ let z := sub(y, x)
+ sstore(add(x, 0), z)
+}
+// ----
+// Assembly:
+// /* "source":38:39 */
+// 0x00
+// /* "source":109:110 */
+// dup1
+// /* "source":38:39 */
+// dup2
+// /* "source":25:40 */
+// calldataload
+// /* "source":91:111 */
+// sstore
+// /* "source":12:113 */
+// pop
+// Bytecode: 60008081355550
+// Opcodes: PUSH1 0x0 DUP1 DUP2 CALLDATALOAD SSTORE POP
diff --git a/test/libyul/objectCompiler/smoke.yul b/test/libyul/objectCompiler/smoke.yul
new file mode 100644
index 00000000..b2e44d4d
--- /dev/null
+++ b/test/libyul/objectCompiler/smoke.yul
@@ -0,0 +1,5 @@
+{
+}
+// ----
+// Assembly:
+// -- empty bytecode --
diff --git a/test/libyul/objectCompiler/subObject.yul b/test/libyul/objectCompiler/subObject.yul
new file mode 100644
index 00000000..98ea4d07
--- /dev/null
+++ b/test/libyul/objectCompiler/subObject.yul
@@ -0,0 +1,21 @@
+object "a" {
+ code {}
+ // Unreferenced data is not added to the assembled bytecode.
+ data "str" "Hello, World!"
+ object "sub" { code { sstore(0, 1) } }
+}
+// ----
+// Assembly:
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+//
+// sub_0: assembly {
+// /* "source":149:150 */
+// 0x01
+// /* "source":146:147 */
+// 0x00
+// /* "source":139:151 */
+// sstore
+// }
+// Bytecode: fe
+// Opcodes: INVALID
diff --git a/test/libyul/objectCompiler/subSubObject.yul b/test/libyul/objectCompiler/subSubObject.yul
new file mode 100644
index 00000000..5e01f6dd
--- /dev/null
+++ b/test/libyul/objectCompiler/subSubObject.yul
@@ -0,0 +1,39 @@
+object "a" {
+ code {}
+ // Unreferenced data is not added to the assembled bytecode.
+ data "str" "Hello, World!"
+ object "sub" {
+ code { sstore(0, 1) }
+ object "subsub" {
+ code { sstore(2, 3) }
+ data "str" hex"123456"
+ }
+ }
+}
+// ----
+// Assembly:
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+//
+// sub_0: assembly {
+// /* "source":153:154 */
+// 0x01
+// /* "source":150:151 */
+// 0x00
+// /* "source":143:155 */
+// sstore
+// stop
+//
+// sub_0: assembly {
+// /* "source":203:204 */
+// 0x03
+// /* "source":200:201 */
+// 0x02
+// /* "source":193:205 */
+// sstore
+// stop
+// data_6adf031833174bbe4c85eafe59ddb54e6584648c2c962c6f94791ab49caa0ad4 123456
+// }
+// }
+// Bytecode: fe
+// Opcodes: INVALID
diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul
new file mode 100644
index 00000000..5283ef9a
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul
@@ -0,0 +1,23 @@
+{
+ function f() -> x {
+ // can re-use x
+ let y := 0
+ mstore(y, 7)
+ }
+ let a
+ // can re-use a
+ let b := 0
+ sstore(a, b)
+}
+// ----
+// commonSubexpressionEliminator
+// {
+// function f() -> x
+// {
+// let y := x
+// mstore(x, 7)
+// }
+// let a
+// let b := a
+// sstore(a, a)
+// }
diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul
new file mode 100644
index 00000000..a790ca65
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul
@@ -0,0 +1,14 @@
+{
+ // This does not replace b by a because there is no
+ // explicit assignment, even though both hold the same value.
+ let a
+ let b
+ mstore(sub(a, b), 7)
+}
+// ----
+// commonSubexpressionEliminator
+// {
+// let a
+// let b
+// mstore(sub(a, b), 7)
+// }
diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul
new file mode 100644
index 00000000..51f5df40
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul
@@ -0,0 +1,14 @@
+{
+ function f() -> x, z {}
+ let c, d := f()
+ let y := add(d, add(c, 7))
+}
+// ----
+// expressionSimplifier
+// {
+// function f() -> x, z
+// {
+// }
+// let c, d := f()
+// let y := add(add(d, c), 7)
+// }
diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul
new file mode 100644
index 00000000..46f9bc40
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul
@@ -0,0 +1,14 @@
+// return variables are assumed to be zero initially.
+{
+ function f() -> c, d {
+ let y := add(d, add(c, 7))
+ }
+}
+// ----
+// expressionSimplifier
+// {
+// function f() -> c, d
+// {
+// let y := 7
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul
new file mode 100644
index 00000000..f260db0b
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul
@@ -0,0 +1,11 @@
+// Unassigned variables are assumed to be zero.
+{
+ let c, d
+ let y := add(d, add(c, 7))
+}
+// ----
+// expressionSimplifier
+// {
+// let c, d
+// let y := 7
+// }
diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul
new file mode 100644
index 00000000..7b1430f3
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul
@@ -0,0 +1,13 @@
+// Unassigned variables are assumed to be zero.
+{
+ let c
+ let d
+ let y := add(d, add(c, 7))
+}
+// ----
+// expressionSimplifier
+// {
+// let c
+// let d
+// let y := 7
+// }
diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul
new file mode 100644
index 00000000..3f7aa089
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul
@@ -0,0 +1,48 @@
+{
+ let random := 42
+ for {
+ for { let a := 1} iszero(eq(a,10)) {} {
+ a := add(a, 1)
+ }
+ let b := 1
+ } iszero(eq(b, 10)) {
+ for { let c := 1 } iszero(eq(c,2)) { c := add(c, 1) } {
+ b := add(b, 1)
+ }
+ } {
+ mstore(b,b)
+ }
+}
+// ----
+// forLoopInitRewriter
+// {
+// let random := 42
+// let a := 1
+// for {
+// }
+// iszero(eq(a, 10))
+// {
+// }
+// {
+// a := add(a, 1)
+// }
+// let b := 1
+// for {
+// }
+// iszero(eq(b, 10))
+// {
+// let c := 1
+// for {
+// }
+// iszero(eq(c, 2))
+// {
+// c := add(c, 1)
+// }
+// {
+// b := add(b, 1)
+// }
+// }
+// {
+// mstore(b, b)
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul
index ee7f5bf5..b3f51593 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul
@@ -9,14 +9,14 @@
// {
// {
// let f_a := calldataload(0)
-// let f_b
-// let f_c
+// let f_b := 0
+// let f_c := 0
// f_b := sload(mload(f_a))
// f_c := 3
// let b3 := f_b
// let f_a_2 := f_c
-// let f_b_3
-// let f_c_4
+// let f_b_3 := 0
+// let f_c_4 := 0
// f_b_3 := sload(mload(f_a_2))
// f_c_4 := 3
// let b4 := f_b_3
diff --git a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul
index 00bb6577..9e6c4e76 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul
@@ -14,7 +14,7 @@
// {
// let _2 := mload(0)
// let f_a := mload(1)
-// let f_r
+// let f_r := 0
// f_a := mload(f_a)
// f_r := add(f_a, calldatasize())
// if gt(f_r, _2)
diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul
index 0972ac56..c00b1163 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul
@@ -2,8 +2,6 @@
function f(a) -> b {
let x := mload(a)
b := sload(x)
- let c := 3
- mstore(mul(a, b), mload(x))
let y := add(a, x)
sstore(y, 10)
}
@@ -13,6 +11,9 @@
let r := f(a)
// This should be inlined because it is a constant
let t := f(a2)
+ let a3
+ // This should be inlined because it is a constant as well (zero)
+ let s := f(a3)
}
// ----
// fullInliner
@@ -22,21 +23,25 @@
// let a2 := 2
// let r := f(a_1)
// let f_a := a2
-// let f_b
+// let f_b := 0
// let f_x := mload(f_a)
// f_b := sload(f_x)
-// let f_c := 3
-// mstore(mul(f_a, f_b), mload(f_x))
// let f_y := add(f_a, f_x)
// sstore(f_y, 10)
// let t := f_b
+// let a3
+// let f_a_3 := a3
+// let f_b_4 := 0
+// let f_x_5 := mload(f_a_3)
+// f_b_4 := sload(f_x_5)
+// let f_y_6 := add(f_a_3, f_x_5)
+// sstore(f_y_6, 10)
+// let s := f_b_4
// }
// function f(a) -> b
// {
// let x := mload(a)
// b := sload(x)
-// let c := 3
-// mstore(mul(a, b), mload(x))
// let y := add(a, x)
// sstore(y, 10)
// }
diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul
index 3302a35c..72beb8b1 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul
@@ -15,7 +15,7 @@
// {
// {
// let f_a := mload(1)
-// let f_b
+// let f_b := 0
// let f_x := mload(f_a)
// f_b := sload(f_x)
// let f_c := 3
diff --git a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul
index 644e9126..869abbc9 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul
@@ -13,7 +13,7 @@
// {
// let verylongvariablename2_1 := 3
// let verylongfu_verylongvariablename := verylongvariablename2_1
-// let verylongfu_verylongvariablename2
+// let verylongfu_verylongvariablename2 := 0
// verylongfu_verylongvariablename2 := add(verylongfu_verylongvariablename, verylongfu_verylongvariablename)
// mstore(0, verylongfu_verylongvariablename2)
// mstore(1, verylongvariablename2_1)
diff --git a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul
index f3d0b286..0b46a3c6 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul
@@ -15,7 +15,7 @@
// let f_a := mload(2)
// let f_b := _6
// let f_c := _4
-// let f_x
+// let f_x := 0
// f_x := add(f_a, f_b)
// f_x := mul(f_x, f_c)
// let _10 := add(f_x, _2)
diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul
index 8bc6ec58..5d658e96 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul
@@ -9,11 +9,11 @@
// {
// let _1 := 7
// let f_a := 3
-// let f_x
+// let f_x := 0
// f_x := add(f_a, f_a)
// let g_b := f_x
// let g_c := _1
-// let g_y
+// let g_y := 0
// g_y := mul(mload(g_c), f(g_b))
// let y_1 := g_y
// }
@@ -24,7 +24,7 @@
// function g(b, c) -> y
// {
// let f_a_6 := b
-// let f_x_7
+// let f_x_7 := 0
// f_x_7 := add(f_a_6, f_a_6)
// y := mul(mload(c), f_x_7)
// }
diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul
index 19ac945e..6e4acb97 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul
@@ -37,7 +37,7 @@
// function f(x)
// {
// mstore(0, x)
-// let h_t
+// let h_t := 0
// h_t := 2
// mstore(7, h_t)
// let g_x_1 := 10
diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul
index eebdec38..a4cbbef0 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul
@@ -11,8 +11,8 @@
// {
// {
// let f_a := mload(0)
-// let f_x
-// let f_y
+// let f_x := 0
+// let f_y := 0
// f_x := mul(f_a, f_a)
// f_y := add(f_a, f_x)
// let r := f_x
diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul
new file mode 100644
index 00000000..f59e2c11
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul
@@ -0,0 +1,44 @@
+{
+ function f(a) -> b {
+ let x := mload(a)
+ b := sload(x)
+ }
+ // This will stop inlining at some point because
+ // the function gets too big.
+ function g() -> x {
+ x := f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(2)))))))))))))))))))
+ }
+}
+// ----
+// fullInliner
+// {
+// function f(a) -> b
+// {
+// b := sload(mload(a))
+// }
+// function g() -> x_1
+// {
+// let f_a := 2
+// let f_b := 0
+// f_b := sload(mload(f_a))
+// let f_a_20 := f_b
+// let f_b_21 := 0
+// f_b_21 := sload(mload(f_a_20))
+// let f_a_23 := f_b_21
+// let f_b_24 := 0
+// f_b_24 := sload(mload(f_a_23))
+// let f_a_26 := f_b_24
+// let f_b_27 := 0
+// f_b_27 := sload(mload(f_a_26))
+// let f_a_29 := f_b_27
+// let f_b_30 := 0
+// f_b_30 := sload(mload(f_a_29))
+// let f_a_32 := f_b_30
+// let f_b_33 := 0
+// f_b_33 := sload(mload(f_a_32))
+// let f_a_35 := f_b_33
+// let f_b_36 := 0
+// f_b_36 := sload(mload(f_a_35))
+// x_1 := f(f(f(f(f(f(f(f(f(f(f(f(f_b_36))))))))))))
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul
new file mode 100644
index 00000000..f20b7221
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul
@@ -0,0 +1,41 @@
+{
+ function f(a) -> b {
+ let x := mload(a)
+ b := sload(x)
+ }
+ // This will stop inlining at some point because
+ // the global context gets too big.
+ let x := f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(2)))))))))))))))))))
+}
+// ----
+// fullInliner
+// {
+// {
+// let f_a := 2
+// let f_b := 0
+// f_b := sload(mload(f_a))
+// let f_a_20 := f_b
+// let f_b_21 := 0
+// f_b_21 := sload(mload(f_a_20))
+// let f_a_23 := f_b_21
+// let f_b_24 := 0
+// f_b_24 := sload(mload(f_a_23))
+// let f_a_26 := f_b_24
+// let f_b_27 := 0
+// f_b_27 := sload(mload(f_a_26))
+// let f_a_29 := f_b_27
+// let f_b_30 := 0
+// f_b_30 := sload(mload(f_a_29))
+// let f_a_32 := f_b_30
+// let f_b_33 := 0
+// f_b_33 := sload(mload(f_a_32))
+// let f_a_35 := f_b_33
+// let f_b_36 := 0
+// f_b_36 := sload(mload(f_a_35))
+// let x_1 := f(f(f(f(f(f(f(f(f(f(f(f(f_b_36))))))))))))
+// }
+// function f(a) -> b
+// {
+// b := sload(mload(a))
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul
index 9644e6c1..96d806bd 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul
@@ -14,7 +14,7 @@
// {
// for {
// let f_a := 0
-// let f_r
+// let f_r := 0
// sstore(f_a, 0)
// f_r := f_a
// let x := f_r
@@ -22,14 +22,14 @@
// f(x)
// {
// let f_a_3 := x
-// let f_r_4
+// let f_r_4 := 0
// sstore(f_a_3, 0)
// f_r_4 := f_a_3
// x := f_r_4
// }
// {
// let f_a_6 := x
-// let f_r_7
+// let f_r_7 := 0
// sstore(f_a_6, 0)
// f_r_7 := f_a_6
// let t := f_r_7
diff --git a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul
index cd9e2746..c1bed208 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul
@@ -14,7 +14,7 @@
// {
// let _1 := 2
// let f_a := 7
-// let f_x
+// let f_x := 0
// let f_r := mul(f_a, f_a)
// f_x := add(f_r, f_r)
// pop(add(f_x, _1))
diff --git a/test/libyul/yulOptimizerTests/fullInliner/simple.yul b/test/libyul/yulOptimizerTests/fullInliner/simple.yul
index fcdf453b..1ada8f53 100644
--- a/test/libyul/yulOptimizerTests/fullInliner/simple.yul
+++ b/test/libyul/yulOptimizerTests/fullInliner/simple.yul
@@ -11,7 +11,7 @@
// {
// let _2 := mload(7)
// let f_a := sload(mload(2))
-// let f_x
+// let f_x := 0
// let f_r := mul(f_a, f_a)
// f_x := add(f_r, f_r)
// let y := add(f_x, _2)
diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul
index a34da198..efb846f2 100644
--- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul
+++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul
@@ -465,178 +465,166 @@
// let abi_encode_pos := _1
// let abi_encode_length_68 := mload(_485)
// mstore(_1, abi_encode_length_68)
-// let abi_encode_pos_590 := 64
-// abi_encode_pos := abi_encode_pos_590
+// abi_encode_pos := 64
// let abi_encode_srcPtr := add(_485, _1)
+// let abi_encode_i_69 := _2
// for {
-// let abi_encode_i_69 := _2
// }
// lt(abi_encode_i_69, abi_encode_length_68)
// {
// abi_encode_i_69 := add(abi_encode_i_69, 1)
// }
// {
-// let _931 := mload(abi_encode_srcPtr)
-// let abi_encode_pos_71_1037 := abi_encode_pos
-// let abi_encode_length_72_1038 := 0x3
-// let abi_encode_srcPtr_73_1039 := _931
+// let _863 := mload(abi_encode_srcPtr)
+// let abi_encode_pos_71_971 := abi_encode_pos
+// let abi_encode_length_72_972 := 0x3
+// let abi_encode_srcPtr_73_973 := _863
+// let abi_encode_i_74_974 := _2
// for {
-// let abi_encode_i_74_1040 := _2
// }
-// lt(abi_encode_i_74_1040, abi_encode_length_72_1038)
+// lt(abi_encode_i_74_974, abi_encode_length_72_972)
// {
-// abi_encode_i_74_1040 := add(abi_encode_i_74_1040, 1)
+// abi_encode_i_74_974 := add(abi_encode_i_74_974, 1)
// }
// {
-// mstore(abi_encode_pos_71_1037, and(mload(abi_encode_srcPtr_73_1039), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
-// abi_encode_srcPtr_73_1039 := add(abi_encode_srcPtr_73_1039, _1)
-// abi_encode_pos_71_1037 := add(abi_encode_pos_71_1037, _1)
+// mstore(abi_encode_pos_71_971, and(mload(abi_encode_srcPtr_73_973), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
+// abi_encode_srcPtr_73_973 := add(abi_encode_srcPtr_73_973, _1)
+// abi_encode_pos_71_971 := add(abi_encode_pos_71_971, _1)
// }
// abi_encode_srcPtr := add(abi_encode_srcPtr, _1)
// abi_encode_pos := add(abi_encode_pos, 0x60)
// }
-// let _933 := 0x40
-// let _487 := mload(_933)
-// let _488 := mload(_1)
-// let abi_decode_value0_60_618
-// let abi_decode_value0_60 := abi_decode_value0_60_618
-// let abi_decode_value1_61_619
-// let abi_decode_value1_61 := abi_decode_value1_61_619
-// let abi_decode_value2_620
-// let abi_decode_value2 := abi_decode_value2_620
-// let abi_decode_value3_621
-// let abi_decode_value3 := abi_decode_value3_621
-// if slt(sub(_487, _488), 128)
+// let a, b, c, d := abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(mload(_1), mload(0x40))
+// sstore(a, b)
+// sstore(c, d)
+// sstore(_2, abi_encode_pos)
+// }
+// function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5
+// {
+// if iszero(slt(add(offset_3, 0x1f), end_4))
// {
-// revert(_2, _2)
+// revert(array_5, array_5)
// }
+// let length_6 := calldataload(offset_3)
+// let array_5_254 := allocateMemory(array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length_6))
+// array_5 := array_5_254
+// let dst_7 := array_5_254
+// mstore(array_5_254, length_6)
+// let _36 := 0x20
+// let offset_3_256 := add(offset_3, _36)
+// dst_7 := add(array_5_254, _36)
+// let src_8 := offset_3_256
+// let _38 := 0x40
+// if gt(add(add(offset_3, mul(length_6, _38)), _36), end_4)
// {
-// abi_decode_value0_60 := calldataload(_488)
+// let _42 := 0
+// revert(_42, _42)
+// }
+// let i_9 := 0
+// for {
// }
+// lt(i_9, length_6)
// {
-// abi_decode_value1_61 := calldataload(add(_488, 32))
+// i_9 := add(i_9, 1)
// }
// {
-// let abi_decode_offset_64 := calldataload(add(_488, abi_encode_pos_590))
-// let _940 := 0xffffffffffffffff
-// if gt(abi_decode_offset_64, _940)
-// {
-// revert(_2, _2)
-// }
-// let _942 := add(_488, abi_decode_offset_64)
-// if iszero(slt(add(_942, 0x1f), _487))
-// {
-// revert(_2, _2)
-// }
-// let abi_decode_length_30_1046 := calldataload(_942)
-// if gt(abi_decode_length_30_1046, _940)
-// {
-// revert(_2, _2)
-// }
-// let abi_decode_array_allo__561 := mul(abi_decode_length_30_1046, _1)
-// let abi_decode_array_29_279_1047 := allocateMemory(add(abi_decode_array_allo__561, _1))
-// let abi_decode_dst_31_1048 := abi_decode_array_29_279_1047
-// mstore(abi_decode_array_29_279_1047, abi_decode_length_30_1046)
-// let abi_decode_offset_27_281_1049 := add(_942, _1)
-// abi_decode_dst_31_1048 := add(abi_decode_array_29_279_1047, _1)
-// let abi_decode_src_32_1050 := abi_decode_offset_27_281_1049
-// if gt(add(add(_942, abi_decode_array_allo__561), _1), _487)
-// {
-// revert(_2, _2)
-// }
-// for {
-// let abi_decode_i_33_1052 := _2
-// }
-// lt(abi_decode_i_33_1052, abi_decode_length_30_1046)
-// {
-// abi_decode_i_33_1052 := add(abi_decode_i_33_1052, 1)
-// }
-// {
-// mstore(abi_decode_dst_31_1048, calldataload(abi_decode_src_32_1050))
-// abi_decode_dst_31_1048 := add(abi_decode_dst_31_1048, _1)
-// abi_decode_src_32_1050 := add(abi_decode_src_32_1050, _1)
-// }
-// abi_decode_value2 := abi_decode_array_29_279_1047
+// mstore(dst_7, abi_decode_t_array$_t_uint256_$2_memory(src_8, end_4))
+// dst_7 := add(dst_7, _36)
+// src_8 := add(src_8, _38)
// }
+// }
+// function abi_decode_t_array$_t_uint256_$2_memory(offset_11, end_12) -> array_13
+// {
+// if iszero(slt(add(offset_11, 0x1f), end_12))
// {
-// let abi_decode_offset_65 := calldataload(add(_488, 96))
-// let _945 := 0xffffffffffffffff
-// if gt(abi_decode_offset_65, _945)
-// {
-// revert(_2, _2)
-// }
-// let _947 := add(_488, abi_decode_offset_65)
-// let abi_decode__489_1056 := 0x1f
-// if iszero(slt(add(_947, abi_decode__489_1056), _487))
-// {
-// revert(_2, _2)
-// }
-// let abi_decode_length_6_1058 := calldataload(_947)
-// if gt(abi_decode_length_6_1058, _945)
-// {
-// revert(_2, _2)
-// }
-// let abi_decode_array_5_254_1061 := allocateMemory(add(mul(abi_decode_length_6_1058, _1), _1))
-// let abi_decode_dst_7_1062 := abi_decode_array_5_254_1061
-// mstore(abi_decode_array_5_254_1061, abi_decode_length_6_1058)
-// let abi_decode_offset_3_256_1063 := add(_947, _1)
-// abi_decode_dst_7_1062 := add(abi_decode_array_5_254_1061, _1)
-// let abi_decode_src_8_1064 := abi_decode_offset_3_256_1063
-// if gt(add(add(_947, mul(abi_decode_length_6_1058, _933)), _1), _487)
-// {
-// revert(_2, _2)
-// }
-// for {
-// let abi_decode_i_9_1068 := _2
-// }
-// lt(abi_decode_i_9_1068, abi_decode_length_6_1058)
+// revert(array_13, array_13)
+// }
+// let length_14 := 0x2
+// let array_allo__559 := 0x20
+// let array_allo_size_95_605 := 64
+// let array_13_263 := allocateMemory(array_allo_size_95_605)
+// array_13 := array_13_263
+// let dst_15 := array_13_263
+// let src_16 := offset_11
+// if gt(add(offset_11, array_allo_size_95_605), end_12)
+// {
+// let _59 := 0
+// revert(_59, _59)
+// }
+// let i_17 := 0
+// for {
+// }
+// lt(i_17, length_14)
+// {
+// i_17 := add(i_17, 1)
+// }
+// {
+// mstore(dst_15, calldataload(src_16))
+// dst_15 := add(dst_15, array_allo__559)
+// src_16 := add(src_16, array_allo__559)
+// }
+// }
+// function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29
+// {
+// if iszero(slt(add(offset_27, 0x1f), end_28))
+// {
+// revert(array_29, array_29)
+// }
+// let length_30 := calldataload(offset_27)
+// let array_29_279 := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length_30))
+// array_29 := array_29_279
+// let dst_31 := array_29_279
+// mstore(array_29_279, length_30)
+// let _91 := 0x20
+// let offset_27_281 := add(offset_27, _91)
+// dst_31 := add(array_29_279, _91)
+// let src_32 := offset_27_281
+// if gt(add(add(offset_27, mul(length_30, _91)), _91), end_28)
+// {
+// let _97 := 0
+// revert(_97, _97)
+// }
+// let i_33 := 0
+// for {
+// }
+// lt(i_33, length_30)
+// {
+// i_33 := add(i_33, 1)
+// }
+// {
+// mstore(dst_31, calldataload(src_32))
+// dst_31 := add(dst_31, _91)
+// src_32 := add(src_32, _91)
+// }
+// }
+// function abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(headStart_58, dataEnd_59) -> value0_60, value1_61, value2, value3
+// {
+// if slt(sub(dataEnd_59, headStart_58), 128)
+// {
+// revert(value2, value2)
+// }
+// {
+// value0_60 := calldataload(add(headStart_58, value2))
+// }
+// {
+// value1_61 := calldataload(add(headStart_58, 32))
+// }
+// {
+// let offset_64 := calldataload(add(headStart_58, 64))
+// if gt(offset_64, 0xffffffffffffffff)
// {
-// abi_decode_i_9_1068 := add(abi_decode_i_9_1068, 1)
+// revert(value2, value2)
// }
+// value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(headStart_58, offset_64), dataEnd_59)
+// }
+// {
+// let offset_65 := calldataload(add(headStart_58, 96))
+// if gt(offset_65, 0xffffffffffffffff)
// {
-// if iszero(slt(add(abi_decode_src_8_1064, abi_decode__489_1056), _487))
-// {
-// revert(_2, _2)
-// }
-// let abi_decode_abi_decode_length_14_1069 := 0x2
-// if _2
-// {
-// revert(_2, _2)
-// }
-// let allocateMe_memPtr_315 := mload(abi_encode_pos_590)
-// let allocateMe_newFreePtr := add(allocateMe_memPtr_315, abi_encode_pos_590)
-// if or(gt(allocateMe_newFreePtr, _945), lt(allocateMe_newFreePtr, allocateMe_memPtr_315))
-// {
-// revert(_2, _2)
-// }
-// mstore(abi_encode_pos_590, allocateMe_newFreePtr)
-// let abi_decode_abi_decode_dst_15_1071 := allocateMe_memPtr_315
-// let abi_decode_abi_decode_src_16_1072 := abi_decode_src_8_1064
-// if gt(add(abi_decode_src_8_1064, abi_encode_pos_590), _487)
-// {
-// revert(_2, _2)
-// }
-// for {
-// let abi_decode_abi_decode_i_17_1073 := _2
-// }
-// lt(abi_decode_abi_decode_i_17_1073, abi_decode_abi_decode_length_14_1069)
-// {
-// abi_decode_abi_decode_i_17_1073 := add(abi_decode_abi_decode_i_17_1073, 1)
-// }
-// {
-// mstore(abi_decode_abi_decode_dst_15_1071, calldataload(abi_decode_abi_decode_src_16_1072))
-// abi_decode_abi_decode_dst_15_1071 := add(abi_decode_abi_decode_dst_15_1071, _1)
-// abi_decode_abi_decode_src_16_1072 := add(abi_decode_abi_decode_src_16_1072, _1)
-// }
-// mstore(abi_decode_dst_7_1062, allocateMe_memPtr_315)
-// abi_decode_dst_7_1062 := add(abi_decode_dst_7_1062, _1)
-// abi_decode_src_8_1064 := add(abi_decode_src_8_1064, _933)
+// revert(value3, value3)
// }
-// abi_decode_value3 := abi_decode_array_5_254_1061
+// value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(headStart_58, offset_65), dataEnd_59)
// }
-// sstore(abi_decode_value0_60, abi_decode_value1_61)
-// sstore(abi_decode_value2, abi_decode_value3)
-// sstore(_2, abi_encode_pos)
// }
// function allocateMemory(size) -> memPtr
// {
@@ -651,4 +639,22 @@
// }
// mstore(_199, newFreePtr)
// }
+// function array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length_92) -> size_93
+// {
+// if gt(length_92, 0xffffffffffffffff)
+// {
+// revert(size_93, size_93)
+// }
+// let _217 := 0x20
+// size_93 := add(mul(length_92, _217), _217)
+// }
+// function array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length_98) -> size_99
+// {
+// if gt(length_98, 0xffffffffffffffff)
+// {
+// revert(size_99, size_99)
+// }
+// let _234 := 0x20
+// size_99 := add(mul(length_98, _234), _234)
+// }
// }
diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul
index deb02068..fbe243d4 100644
--- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul
+++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul
@@ -9,16 +9,23 @@
pop(allocate(0x20))
let x := allocate(0x40)
mstore(array_index_access(x, 3), 2)
+ if 0 {
+ mstore(0x40, 0x20)
+ }
+ if sub(2,1) {
+ for { switch mul(1,2) case 2 { mstore(0x40, 0x20) } } sub(1,1) {} { mstore(0x80, 0x40) }
+ }
}
// ----
// fullSuite
// {
// {
-// let _18 := 0x20
-// let allocate__7 := 0x40
-// mstore(allocate__7, add(mload(allocate__7), _18))
-// let allocate_p_12_31 := mload(allocate__7)
-// mstore(allocate__7, add(allocate_p_12_31, allocate__7))
-// mstore(add(allocate_p_12_31, 96), 2)
+// let _1 := 0x20
+// let allocate__19 := 0x40
+// mstore(allocate__19, add(mload(allocate__19), _1))
+// let allocate_p_24_41 := mload(allocate__19)
+// mstore(allocate__19, add(allocate_p_24_41, allocate__19))
+// mstore(add(allocate_p_24_41, 96), 2)
+// mstore(allocate__19, _1)
// }
// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_movable_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_movable_condition.yul
new file mode 100644
index 00000000..ee1975e7
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_movable_condition.yul
@@ -0,0 +1,7 @@
+{ let a := mload(0) if a {} }
+// ----
+// structuralSimplifier
+// {
+// let a := mload(0)
+// pop(a)
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_non_movable_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_non_movable_condition.yul
new file mode 100644
index 00000000..5977297b
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/empty_if_non_movable_condition.yul
@@ -0,0 +1,6 @@
+{ if mload(0) {} }
+// ----
+// structuralSimplifier
+// {
+// pop(mload(0))
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol b/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol
new file mode 100644
index 00000000..b881a0a3
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol
@@ -0,0 +1,10 @@
+{
+ for { let a := 42 } 0 { a := a } {
+ let b := a
+ }
+}
+// ----
+// structuralSimplifier
+// {
+// let a := 42
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul
new file mode 100644
index 00000000..0895b1bb
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul
@@ -0,0 +1,5 @@
+{ if 0 { mstore(0, 0) } }
+// ----
+// structuralSimplifier
+// {
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul
new file mode 100644
index 00000000..0ece5dbd
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul
@@ -0,0 +1,10 @@
+{
+ let x, y
+ if x { mstore(0, 0) }
+ if y { mstore(0, 0) }
+}
+// ----
+// structuralSimplifier
+// {
+// let x, y
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul
new file mode 100644
index 00000000..ca9cba06
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul
@@ -0,0 +1,6 @@
+{ if 1 { mstore(0, 0) } }
+// ----
+// structuralSimplifier
+// {
+// mstore(0, 0)
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul
new file mode 100644
index 00000000..a327a882
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul
@@ -0,0 +1,9 @@
+{
+ let x
+ if x { mstore(0, 0) }
+}
+// ----
+// structuralSimplifier
+// {
+// let x
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul
new file mode 100644
index 00000000..169a84d1
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul
@@ -0,0 +1,6 @@
+{ if 1 { if 1 { for { mstore(0, 0) } 0 {} { mstore(2, 3) } if 0 { mstore(1, 2) } } } }
+// ----
+// structuralSimplifier
+// {
+// mstore(0, 0)
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_only_default.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_only_default.yul
new file mode 100644
index 00000000..7ca815a7
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_only_default.yul
@@ -0,0 +1,11 @@
+{
+ switch mload(0) default { mstore(1, 2) }
+}
+// ----
+// structuralSimplifier
+// {
+// pop(mload(0))
+// {
+// mstore(1, 2)
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_to_if.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_to_if.yul
new file mode 100644
index 00000000..a741ac2f
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_to_if.yul
@@ -0,0 +1,11 @@
+{
+ switch 1 case 2 { mstore(0, 0) }
+}
+// ----
+// structuralSimplifier
+// {
+// if eq(2, 1)
+// {
+// mstore(0, 0)
+// }
+// }
diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul
new file mode 100644
index 00000000..5e2d60c2
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul
@@ -0,0 +1,29 @@
+{
+ // This component does not need the disambiguator
+ function f() -> x, y {
+ let a, b
+ mstore(a, b)
+ let d
+ d := 2
+ }
+ let a
+ a := 4
+ let b := 2
+ let x, y := f()
+}
+// ----
+// varDeclInitializer
+// {
+// function f() -> x, y
+// {
+// let a := 0
+// let b := 0
+// mstore(a, b)
+// let d := 0
+// d := 2
+// }
+// let a := 0
+// a := 4
+// let b := 2
+// let x, y := f()
+// }
diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul
new file mode 100644
index 00000000..16428d7e
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul
@@ -0,0 +1,24 @@
+{
+ function f() -> x, y {
+ let a, b
+ mstore(a, b)
+ let d
+ d := 2
+ }
+ let r
+ r := 4
+}
+// ----
+// varDeclInitializer
+// {
+// function f() -> x, y
+// {
+// let a := 0
+// let b := 0
+// mstore(a, b)
+// let d := 0
+// d := 2
+// }
+// let r := 0
+// r := 4
+// }
diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul
new file mode 100644
index 00000000..02d731af
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul
@@ -0,0 +1,14 @@
+{
+ let x, y, z
+ let a
+ let b
+}
+// ----
+// varDeclInitializer
+// {
+// let x := 0
+// let y := 0
+// let z := 0
+// let a := 0
+// let b := 0
+// }
diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul
new file mode 100644
index 00000000..2e14fe70
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul
@@ -0,0 +1,21 @@
+{
+ function f() -> x, y {
+ let a, b := f()
+ let u
+ }
+ let r
+ let s := 3
+ let t
+}
+// ----
+// varDeclInitializer
+// {
+// function f() -> x, y
+// {
+// let a, b := f()
+// let u := 0
+// }
+// let r := 0
+// let s := 3
+// let t := 0
+// }
diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul
new file mode 100644
index 00000000..2a9bbe42
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul
@@ -0,0 +1,8 @@
+{
+ let a
+}
+// ----
+// varDeclInitializer
+// {
+// let a := 0
+// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul
deleted file mode 100644
index 54fea2fb..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- let a := 4
- let x
- if a {
- x := 2
- }
-}
-// ----
-// varDeclPropagator
-// {
-// let a := 4
-// let x
-// if a
-// {
-// x := 2
-// }
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul
deleted file mode 100644
index ed8d33b4..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- function f() -> a, b, c {}
- let x, y, z
- z, x, y := f()
-}
-// ----
-// varDeclPropagator
-// {
-// function f() -> a, b, c
-// {
-// }
-// let z, x, y := f()
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul
deleted file mode 100644
index ca921500..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- let a
- a := 4
- a := 5
-}
-// ----
-// varDeclPropagator
-// {
-// let a := 4
-// a := 5
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul
deleted file mode 100644
index 3affcac6..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- let a, b
- a := mload(0)
-}
-// ----
-// varDeclPropagator
-// {
-// let b
-// let a := mload(0)
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul
deleted file mode 100644
index d8959040..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- let f
- f := mload(0)
-}
-// ----
-// varDeclPropagator
-// {
-// let f := mload(0)
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul
deleted file mode 100644
index e8c91e10..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- let a, b
- a := mload(0)
- b := mload(1)
-}
-// ----
-// varDeclPropagator
-// {
-// let a := mload(0)
-// let b := mload(1)
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul
deleted file mode 100644
index 5312112a..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- let b
- let a := b
- b := 1
-}
-// ----
-// varDeclPropagator
-// {
-// let b
-// let a := b
-// b := 1
-// }
diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul
deleted file mode 100644
index e27785dd..00000000
--- a/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- function f(x) {}
- let a
- f(a)
- a := 4
-}
-// ----
-// varDeclPropagator
-// {
-// function f(x)
-// {
-// }
-// let a
-// f(a)
-// a := 4
-// }
diff --git a/test/solcjsTests.sh b/test/solcjsTests.sh
index e0bbc5df..b9224862 100755
--- a/test/solcjsTests.sh
+++ b/test/solcjsTests.sh
@@ -60,7 +60,7 @@ DIR=$(mktemp -d)
# Update version (needed for some tests)
echo "Updating package.json to version $VERSION"
- npm version --no-git-tag-version $VERSION
+ npm version --allow-same-version --no-git-tag-version $VERSION
echo "Running solc-js tests..."
npm run test
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index 736212fc..da8e0b39 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -4,7 +4,19 @@ target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_L
add_executable(yulopti yulopti.cpp)
target_link_libraries(yulopti PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES})
-add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../TestCase.cpp ../libsolidity/SyntaxTest.cpp
- ../libsolidity/AnalysisFramework.cpp ../libsolidity/SolidityExecutionFramework.cpp ../ExecutionFramework.cpp
- ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerJSONTest.cpp ../libyul/YulOptimizerTest.cpp)
+add_executable(isoltest
+ isoltest.cpp
+ ../Options.cpp
+ ../Common.cpp
+ ../TestCase.cpp
+ ../libsolidity/SyntaxTest.cpp
+ ../libsolidity/AnalysisFramework.cpp
+ ../libsolidity/SolidityExecutionFramework.cpp
+ ../ExecutionFramework.cpp
+ ../RPCSession.cpp
+ ../libsolidity/ASTJSONTest.cpp
+ ../libsolidity/SMTCheckerJSONTest.cpp
+ ../libyul/ObjectCompilerTest.cpp
+ ../libyul/YulOptimizerTest.cpp
+)
target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp
index f8e2dc58..e5578045 100644
--- a/test/tools/isoltest.cpp
+++ b/test/tools/isoltest.cpp
@@ -19,10 +19,7 @@
#include <test/Common.h>
#include <test/libsolidity/AnalysisFramework.h>
-#include <test/libsolidity/SyntaxTest.h>
-#include <test/libsolidity/ASTJSONTest.h>
-#include <test/libsolidity/SMTCheckerJSONTest.h>
-#include <test/libyul/YulOptimizerTest.h>
+#include <test/InteractiveTests.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -379,48 +376,13 @@ Allowed options)",
TestStats global_stats{0, 0};
// Actually run the tests.
- // If you add new tests here, you also have to add them in boostTest.cpp
- if (auto stats = runTestSuite("Syntax", testPath / "libsolidity", "syntaxTests", SyntaxTest::create, formatted))
- global_stats += *stats;
- else
- return 1;
-
- if (auto stats = runTestSuite("JSON AST", testPath / "libsolidity", "ASTJSON", ASTJSONTest::create, formatted))
- global_stats += *stats;
- else
- return 1;
-
- if (auto stats = runTestSuite(
- "Yul Optimizer",
- testPath / "libyul",
- "yulOptimizerTests",
- yul::test::YulOptimizerTest::create,
- formatted
- ))
- global_stats += *stats;
- else
- return 1;
-
- if (!disableSMT)
+ // Interactive tests are added in InteractiveTests.h
+ for (auto const& ts: g_interactiveTestsuites)
{
- if (auto stats = runTestSuite(
- "SMT Checker",
- testPath / "libsolidity",
- "smtCheckerTests",
- SyntaxTest::create,
- formatted
- ))
- global_stats += *stats;
- else
- return 1;
+ if (ts.smt && disableSMT)
+ continue;
- if (auto stats = runTestSuite(
- "SMT Checker JSON",
- testPath / "libsolidity",
- "smtCheckerTestsJSON",
- SMTCheckerTest::create,
- formatted
- ))
+ if (auto stats = runTestSuite(ts.title, testPath / ts.path, ts.subpath, ts.testCaseCreator, formatted))
global_stats += *stats;
else
return 1;
diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp
index 5273bbb9..fcbe308f 100644
--- a/test/tools/yulopti.cpp
+++ b/test/tools/yulopti.cpp
@@ -46,7 +46,10 @@
#include <libyul/optimiser/ExpressionJoiner.h>
#include <libyul/optimiser/RedundantAssignEliminator.h>
#include <libyul/optimiser/SSATransform.h>
-#include <libyul/optimiser/VarDeclPropagator.h>
+#include <libyul/optimiser/StructuralSimplifier.h>
+#include <libyul/optimiser/VarDeclInitializer.h>
+
+#include <libyul/backends/evm/EVMDialect.h>
#include <libdevcore/JSON.h>
@@ -67,9 +70,9 @@ namespace po = boost::program_options;
class YulOpti
{
public:
- void printErrors(Scanner const& _scanner)
+ void printErrors()
{
- SourceReferenceFormatter formatter(cout, [&](string const&) -> Scanner const& { return _scanner; });
+ SourceReferenceFormatter formatter(cout);
for (auto const& error: m_errors)
formatter.printExceptionInformation(
@@ -82,11 +85,11 @@ public:
{
ErrorReporter errorReporter(m_errors);
shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input, ""));
- m_ast = yul::Parser(errorReporter, yul::AsmFlavour::Strict).parse(scanner, false);
+ m_ast = yul::Parser(errorReporter, yul::EVMDialect::strictAssemblyForEVM()).parse(scanner, false);
if (!m_ast || !errorReporter.errors().empty())
{
cout << "Error parsing source." << endl;
- printErrors(*scanner);
+ printErrors();
return false;
}
m_analysisInfo = make_shared<yul::AsmAnalysisInfo>();
@@ -94,13 +97,13 @@ public:
*m_analysisInfo,
errorReporter,
EVMVersion::byzantium(),
- boost::none,
- AsmFlavour::Strict
+ langutil::Error::Type::SyntaxError,
+ EVMDialect::strictAssemblyForEVM()
);
if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty())
{
cout << "Error analyzing source." << endl;
- printErrors(*scanner);
+ printErrors();
return false;
}
return true;
@@ -122,9 +125,10 @@ public:
m_nameDispenser = make_shared<NameDispenser>(*m_ast);
disambiguated = true;
}
- cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl;
+ cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl;
cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl;
- cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter? ";
+ cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl;
+ cout << " s(t)ructural simplifier? " << endl;
cout.flush();
int option = readStandardInputChar();
cout << ' ' << char(option) << endl;
@@ -142,7 +146,7 @@ public:
(CommonSubexpressionEliminator{})(*m_ast);
break;
case 'd':
- (VarDeclPropagator{})(*m_ast);
+ (VarDeclInitializer{})(*m_ast);
break;
case 'x':
ExpressionSplitter{*m_nameDispenser}(*m_ast);
@@ -165,6 +169,9 @@ public:
case 's':
ExpressionSimplifier::run(*m_ast);
break;
+ case 't':
+ (StructuralSimplifier{})(*m_ast);
+ break;
case 'u':
UnusedPruner::runUntilStabilised(*m_ast);
break;