diff --git a/.circleci/config.yml b/.circleci/config.yml index 0319ca152..23da90b8f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,6 +122,10 @@ defaults: name: command line tests command: ./test/cmdlineTests.sh + - run_docs_pragma_min_version: &run_docs_pragma_min_version + name: docs pragma version check + command: ./scripts/docs_version_pragma_check.sh + - test_ubuntu1604_clang: &test_ubuntu1604_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1604-clang-ossfuzz-<< pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image-rev >> @@ -303,6 +307,18 @@ jobs: name: Linting Python Scripts command: ./scripts/pylint_all.py + chk_antlr_grammar: + docker: + - image: buildpack-deps:eoan + steps: + - checkout + - run: + name: Install Java + command: apt -q update && apt install -y openjdk-14-jdk + - run: + name: Run tests + command: ./scripts/test_antlr_grammar.sh + chk_buglist: docker: - image: circleci/node @@ -335,6 +351,15 @@ jobs: pip install --user z3-solver - run: *run_proofs + chk_docs_pragma_min_version: + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + environment: + TERM: xterm + steps: + - checkout + - run: *run_docs_pragma_min_version + b_ubu_clang: &build_ubuntu1904_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >> @@ -768,6 +793,8 @@ workflows: - chk_buglist: *workflow_trigger_on_tags - chk_proofs: *workflow_trigger_on_tags - chk_pylint: *workflow_trigger_on_tags + - chk_antlr_grammar: *workflow_trigger_on_tags + - chk_docs_pragma_min_version: *workflow_trigger_on_tags # build-only - b_docs: *workflow_trigger_on_tags diff --git a/.clang-format b/.clang-format index f9953c17b..edd5de4d2 100644 --- a/.clang-format +++ b/.clang-format @@ -6,26 +6,35 @@ # Note that clang-format cannot express the style that closing parentheses # behave similar to closing curly braces in a multi-line setting in that # they have to be on a line of their own at the same indentation level -# as the opening part. +# as the opening part (aka "dangling parenthesis", see https://reviews.llvm.org/D33029). Language: Cpp BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak AlignEscapedNewlinesLeft: true AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: All BreakBeforeBraces: Allman ColumnLimit: 120 ContinuationIndentWidth: 4 +FixNamespaceComments: false IndentWidth: 4 KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 2 PenaltyBreakBeforeFirstCallParameter: 2000 +PointerAlignment: Left SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeCtorInitializerColon: false +SpaceBeforeInheritanceColon: false SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false TabWidth: 4 -UseTab: ForIndentation +UseTab: Always # Local Variables: # mode: yaml diff --git a/.github/ISSUE_TEMPLATE/general.md b/.github/ISSUE_TEMPLATE/general.md index 410b42e00..e69de29bb 100644 --- a/.github/ISSUE_TEMPLATE/general.md +++ b/.github/ISSUE_TEMPLATE/general.md @@ -1,21 +0,0 @@ ---- -name: General Feedback -about: Any general feedback (neither feature request nor bug reports) ---- - - -## Description - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 9fdbf158e..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,22 +0,0 @@ - - -### Description - - - -### Checklist -- [ ] Code compiles correctly -- [ ] All tests are passing -- [ ] New tests have been created which fail without the change (if possible) -- [ ] README / documentation was extended, if necessary -- [ ] Changelog entry (if change is visible to the user) -- [ ] Used meaningful commit messages diff --git a/CMakeLists.txt b/CMakeLists.txt index 4278770f1..dba182345 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,9 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.6.4") +set(PROJECT_VERSION "0.6.5") +# OSX target needed in order to support std::visit +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 6bdb48fba..78b2ab310 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -141,7 +141,7 @@ struct MeanSigma double const d = 0; int i = 0; int j = 0; -char* s; +char* s = nullptr; MeanAndSigma ms meanAndSigma(std::vector const& _v, Accuracy _a); Derived* x = dynamic_cast(base); for (auto i = x->begin(); i != x->end(); ++i) {} diff --git a/Changelog.md b/Changelog.md index 19e89adf5..17d4fd0ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,28 @@ +### 0.6.5 (2020-04-06) + +Important Bugfixes: + * Code Generator: Restrict the length of dynamic memory arrays to 64 bits during creation at runtime fixing a possible overflow. + + +Language Features: + * Allow local storage variables to be declared without initialization, as long as they are assigned before they are accessed. + * State variables can be marked ``immutable`` which causes them to be read-only, but assignable in the constructor. The value will be stored directly in the code. + + +Compiler Features: + * Commandline Interface: Enable output of storage layout with `--storage-layout`. + * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. + + +Bugfixes: + * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. + * Inline Assembly: Fix internal error when accessing invalid constant variables. + * Inline Assembly: Fix internal error when accessing functions. + * JSON AST: Always add pointer suffix for memory reference types. + * Reference Resolver: Fix internal error when accessing invalid struct members. + * Type Checker: Fix internal errors when assigning nested tuples. + + ### 0.6.4 (2020-03-10) Language Features: @@ -18,7 +43,6 @@ Bugfixes: * SMTChecker: Fix internal errors when analysing tuples. * Yul AST Import: correctly import blocks as statements, switch statements and string literals. - ### 0.6.3 (2020-02-18) Language Features: @@ -32,6 +56,7 @@ Compiler Features: * Code Generator: Use ``calldatacopy`` instead of ``codecopy`` to zero out memory past input. * Debug: Provide reason strings for compiler-generated internal reverts when using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting on ``debug`` mode. * Yul Optimizer: Prune functions that call each other but are otherwise unreferenced. + * SMTChecker: CHC support to internal function calls. Bugfixes: @@ -120,6 +145,12 @@ Compiler Features: * ABIEncoderV2: Do not warn about enabled ABIEncoderV2 anymore (the pragma is still needed, though). +### 0.5.17 (2020-03-17) + +Bugfixes: + * Type Checker: Disallow overriding of private functions. + + ### 0.5.16 (2020-01-02) Backported Bugfixes: diff --git a/README.md b/README.md index c0a98aefa..c3636ff06 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Solidity is a statically typed, contract-oriented, high-level language for imple - [Development](#development) - [Maintainers](#maintainers) - [License](#license) +- [Security](#security) ## Background @@ -75,3 +76,7 @@ releases [in the projects section](https://github.com/ethereum/solidity/projects Solidity is licensed under [GNU General Public License v3.0](LICENSE.txt). Some third-party code has its [own licensing terms](cmake/templates/license.h.in). + +## Security + +The security policy may be [found here](SECURITY.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..dd39453e8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,52 @@ +# Security Policy + +The Solidity team and community take all security bugs in Solidity seriously. +We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. + +## Scope + +Bugs in the Solidity repository are in scope. +Bugs in third-party dependencies e.g., jsoncpp, boost etc. are not in scope unless they result in a Solidity specific bug. + +Only bugs that have a demonstrable security impact on smart contracts are in scope. +For example, a Solidity program whose optimization is incorrect (e.g., leads to an incorrect output) qualifies as a security bug. +Please note that the [rules][2] of the [Ethereum bounty program][1] have precedence over this security policy. + +## Supported Versions + +As a general rule, only the latest release gets security updates. +Exceptions may be made when the current breaking release is relatively new, e.g. less than three months old. +If you are reporting a bug, please state clearly the Solidity version(s) it affects. + +Example 1: Assuming the current release is `0.6.3` and a security bug has been found in it that affects both `0.5.x` and `0.6.x` trees, we may not only patch `0.6.3` (the bug-fix release numbered `0.6.4`) but `0.5.x` as well (the bug-fix release numbered `0.5.(x+1)`). + +Example 2: Assuming the current release is `0.6.25` and a security bug has been found in it, we may only patch `0.6.25` (in the bug-fix release numbered `0.6.26`) even if the bug affects a previous tree such as `0.5.x`. + +## Reporting a Vulnerability + +To report a vulnerability, please follow the instructions stated in the [Ethereum bounty program][1]. + +In the bug report, please include all details necessary to reproduce the vulnerability such as: + +- Input program that triggers the bug +- Compiler version affected +- Target EVM version +- Framework/IDE if applicable +- EVM execution environment/client if applicable +- Operating system + +Please include steps to reproduce the bug you have found in as much detail as possible. + +Once we have received your bug report, we will try to reproduce it and provide a more detailed response. +Once the reported bug has been successfully reproduced, the Solidity team will work on a fix. + +The Solidity team maintains the following JSON-formatted lists of patched security vulnerabilities: + +- [Summary of known security vulnerabilities][3] +- [List of security vulnerabilities affecting a specific version of the compiler][4]. + + +[1]: https://bounty.ethereum.org/ +[2]: https://bounty.ethereum.org/#rules +[3]: https://solidity.readthedocs.io/en/develop/bugs.html +[4]: https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index cc4489e9f..b36af7e3f 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -26,6 +26,17 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) # Prevent the path of the source directory from ending up in the binary via __FILE__ macros. eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity") +# -Wpessimizing-move warns when a call to std::move would prevent copy elision +# if the argument was not wrapped in a call. This happens when moving a local +# variable in a return statement when the variable is the same type as the +# return type or using a move to create a new object from a temporary object. +eth_add_cxx_compiler_flag_if_supported(-Wpessimizing-move) + +# -Wredundant-move warns when an implicit move would already be made, so the +# std::move call is not needed, such as when moving a local variable in a return +# that is different from the return type. +eth_add_cxx_compiler_flag_if_supported(-Wredundant-move) + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # 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/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index ede846cc3..9461cb6bd 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -292,8 +292,9 @@ Consider you have the following pre-0.5.0 contract already deployed: :: - // This will not compile with the current version of the compiler pragma solidity ^0.4.25; + // This will report a warning until version 0.4.25 of the compiler + // This will not compile after 0.5.0 contract OldContract { function someOldFunction(uint8 a) { //... @@ -369,8 +370,8 @@ Old version: :: - // This will not compile pragma solidity ^0.4.25; + // This will not compile after 0.5.0 contract OtherContract { uint x; @@ -396,7 +397,7 @@ Old version: // Throw is fine in this version. if (x > 100) throw; - bytes b = new bytes(x); + bytes memory b = new bytes(x); y = -3 >> 1; // y == -1 (wrong, should be -2) do { @@ -431,14 +432,15 @@ New version: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.5.0 <0.5.99; + // This will not compile after 0.6.0 contract OtherContract { uint x; function f(uint y) external { x = y; } - receive() payable external {} + function() payable external {} } contract New { diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 new file mode 100644 index 000000000..8721f47a4 --- /dev/null +++ b/docs/Solidity.g4 @@ -0,0 +1,482 @@ +// Copyright 2020 Gonçalo Sá +// Copyright 2016-2019 Federico Bond +// Licensed under the MIT license. See LICENSE file in the project root for details. + +// This grammar is much less strict than what Solidity currently parses +// to allow this to pass with older versions of Solidity. + +grammar Solidity; + +sourceUnit + : (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ; + +pragmaDirective + : 'pragma' pragmaName pragmaValue ';' ; + +pragmaName + : identifier ; + +pragmaValue + : version | expression ; + +version + : versionConstraint versionConstraint? ; + +versionConstraint + : versionOperator? VersionLiteral ; + +versionOperator + : '^' | '~' | '>=' | '>' | '<' | '<=' | '=' ; + +importDirective + : 'import' StringLiteralFragment ('as' identifier)? ';' + | 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';' + | 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ; + +importDeclaration + : identifier ('as' identifier)? ; + +contractDefinition + : 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier + ( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )? + '{' contractPart* '}' ; + +inheritanceSpecifier + : userDefinedTypeName ( '(' expressionList? ')' )? ; + +contractPart + : stateVariableDeclaration + | usingForDeclaration + | structDefinition + | modifierDefinition + | functionDefinition + | eventDefinition + | enumDefinition ; + +stateVariableDeclaration + : typeName + ( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )* + identifier ('=' expression)? ';' ; + +overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ; + +usingForDeclaration + : 'using' identifier 'for' ('*' | typeName) ';' ; + +structDefinition + : 'struct' identifier + '{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ; + +modifierDefinition + : 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* block ; + +functionDefinition + : functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ; + +functionDescriptor + : 'function' ( identifier | ReceiveKeyword | FallbackKeyword )? + | ConstructorKeyword + | FallbackKeyword + | ReceiveKeyword ; + +returnParameters + : 'returns' parameterList ; + +modifierList + : ( modifierInvocation | stateMutability | ExternalKeyword + | PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ; + +modifierInvocation + : identifier ( '(' expressionList? ')' )? ; + +eventDefinition + : 'event' identifier eventParameterList AnonymousKeyword? ';' ; + +enumDefinition + : 'enum' identifier '{' enumValue? (',' enumValue)* '}' ; + +enumValue + : identifier ; + +parameterList + : '(' ( parameter (',' parameter)* )? ')' ; + +parameter + : typeName storageLocation? identifier? ; + +eventParameterList + : '(' ( eventParameter (',' eventParameter)* )? ')' ; + +eventParameter + : typeName IndexedKeyword? identifier? ; + +variableDeclaration + : typeName storageLocation? identifier ; + +typeName + : elementaryTypeName + | userDefinedTypeName + | mapping + | typeName '[' expression? ']' + | functionTypeName ; + +userDefinedTypeName + : identifier ( '.' identifier )* ; + +mapping + : 'mapping' '(' (elementaryTypeName | userDefinedTypeName) '=>' typeName ')' ; + +functionTypeName + : 'function' parameterList modifierList returnParameters? ; + +storageLocation + : 'memory' | 'storage' | 'calldata'; + +stateMutability + : PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ; + +block + : '{' statement* '}' ; + +statement + : ifStatement + | tryStatement + | whileStatement + | forStatement + | block + | inlineAssemblyStatement + | doWhileStatement + | continueStatement + | breakStatement + | returnStatement + | throwStatement + | emitStatement + | simpleStatement ; + +expressionStatement + : expression ';' ; + +ifStatement + : 'if' '(' expression ')' statement ( 'else' statement )? ; + +tryStatement : 'try' expression returnParameters? block catchClause+ ; + +// In reality catch clauses still are not processed as below +// the identifier can only be a set string: "Error". But plans +// of the Solidity team include possible expansion so we'll +// leave this as is, befitting with the Solidity docs. +catchClause : 'catch' ( identifier? parameterList )? block ; + +whileStatement + : 'while' '(' expression ')' statement ; + +forStatement + : 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ; + +simpleStatement + : ( variableDeclarationStatement | expressionStatement ) ; + +inlineAssemblyStatement + : 'assembly' StringLiteralFragment? assemblyBlock ; + +doWhileStatement + : 'do' statement 'while' '(' expression ')' ';' ; + +continueStatement + : 'continue' ';' ; + +breakStatement + : 'break' ';' ; + +returnStatement + : 'return' expression? ';' ; + +// throw is no longer supported by latest Solidity. +throwStatement + : 'throw' ';' ; + +emitStatement + : 'emit' functionCall ';' ; + +// 'var' is no longer supported by latest Solidity. +variableDeclarationStatement + : ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';'; + +variableDeclarationList + : variableDeclaration? (',' variableDeclaration? )* ; + +identifierList + : '(' ( identifier? ',' )* identifier? ')' ; + +elementaryTypeName + : 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ; + +Int + : 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ; + +Uint + : 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ; + +Byte + : 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ; + +Fixed + : 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ; + +Ufixed + : 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ; + +expression + : expression ('++' | '--') + | 'new' typeName + | expression '[' expression? ']' + | expression '[' expression? ':' expression? ']' + | expression '.' identifier + | expression '{' nameValueList '}' + | expression '(' functionCallArguments ')' + | PayableKeyword '(' expression ')' + | '(' expression ')' + | ('++' | '--') expression + | ('+' | '-') expression + | ('after' | 'delete') expression + | '!' expression + | '~' expression + | expression '**' expression + | expression ('*' | '/' | '%') expression + | expression ('+' | '-') expression + | expression ('<<' | '>>') expression + | expression '&' expression + | expression '^' expression + | expression '|' expression + | expression ('<' | '>' | '<=' | '>=') expression + | expression ('==' | '!=') expression + | expression '&&' expression + | expression '||' expression + | expression '?' expression ':' expression + | expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression + | primaryExpression ; + +primaryExpression + : BooleanLiteral + | numberLiteral + | hexLiteral + | stringLiteral + | identifier ('[' ']')? + | TypeKeyword + | tupleExpression + | typeNameExpression ('[' ']')? ; + +expressionList + : expression (',' expression)* ; + +nameValueList + : nameValue (',' nameValue)* ','? ; + +nameValue + : identifier ':' expression ; + +functionCallArguments + : '{' nameValueList? '}' + | expressionList? ; + +functionCall + : expression '(' functionCallArguments ')' ; + +tupleExpression + : '(' ( expression? ( ',' expression? )* ) ')' + | '[' ( expression ( ',' expression )* )? ']' ; + +typeNameExpression + : elementaryTypeName + | userDefinedTypeName ; + +assemblyItem + : identifier + | assemblyBlock + | assemblyExpression + | assemblyLocalDefinition + | assemblyAssignment + | assemblyStackAssignment + | labelDefinition + | assemblySwitch + | assemblyFunctionDefinition + | assemblyFor + | assemblyIf + | BreakKeyword + | ContinueKeyword + | LeaveKeyword + | subAssembly + | numberLiteral + | stringLiteral + | hexLiteral ; + +assemblyBlock + : '{' assemblyItem* '}' ; + +assemblyExpression + : assemblyCall | assemblyLiteral ; + +assemblyCall + : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; + +assemblyLocalDefinition + : 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ; + +assemblyAssignment + : assemblyIdentifierList ':=' assemblyExpression ; + +assemblyIdentifierList + : identifier ( ',' identifier )* ; + +assemblyStackAssignment + : '=:' identifier ; + +labelDefinition + : identifier ':' ; + +assemblySwitch + : 'switch' assemblyExpression assemblyCase* ; + +assemblyCase + : 'case' assemblyLiteral assemblyType? assemblyBlock + | 'default' assemblyBlock ; + +assemblyFunctionDefinition + : 'function' identifier '(' assemblyTypedVariableList? ')' + assemblyFunctionReturns? assemblyBlock ; + +assemblyFunctionReturns + : ( '-' '>' assemblyTypedVariableList ) ; + +assemblyFor + : 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ; + +assemblyIf + : 'if' assemblyExpression assemblyBlock ; + +assemblyLiteral + : ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ; + +assemblyTypedVariableList + : identifier assemblyType? ( ',' assemblyTypedVariableList )? ; + +assemblyType + : ':' identifier ; + +subAssembly + : 'assembly' identifier assemblyBlock ; + +numberLiteral + : (DecimalNumber | HexNumber) NumberUnit? ; + +identifier + : ('from' | 'calldata' | 'address' | Identifier) ; + +BooleanLiteral + : 'true' | 'false' ; + +DecimalNumber + : ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ; + +fragment +DecimalDigits + : [0-9] ( '_'? [0-9] )* ; + +HexNumber + : '0' [xX] HexDigits ; + +fragment +HexDigits + : HexCharacter ( '_'? HexCharacter )* ; + +NumberUnit + : 'wei' | 'szabo' | 'finney' | 'ether' + | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; + +HexLiteralFragment + : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; + +hexLiteral : HexLiteralFragment+ ; + +fragment +HexPair + : HexCharacter HexCharacter ; + +fragment +HexCharacter + : [0-9A-Fa-f] ; + +ReservedKeyword + : 'after' + | 'case' + | 'default' + | 'final' + | 'in' + | 'inline' + | 'let' + | 'match' + | 'null' + | 'of' + | 'relocatable' + | 'static' + | 'switch' + | 'typeof' ; + +AnonymousKeyword : 'anonymous' ; +BreakKeyword : 'break' ; +ConstantKeyword : 'constant' ; +ImmutableKeyword : 'immutable' ; +ContinueKeyword : 'continue' ; +LeaveKeyword : 'leave' ; +ExternalKeyword : 'external' ; +IndexedKeyword : 'indexed' ; +InternalKeyword : 'internal' ; +PayableKeyword : 'payable' ; +PrivateKeyword : 'private' ; +PublicKeyword : 'public' ; +VirtualKeyword : 'virtual' ; +PureKeyword : 'pure' ; +TypeKeyword : 'type' ; +ViewKeyword : 'view' ; + +ConstructorKeyword : 'constructor' ; +FallbackKeyword : 'fallback' ; +ReceiveKeyword : 'receive' ; + +Identifier + : IdentifierStart IdentifierPart* ; + +fragment +IdentifierStart + : [a-zA-Z$_] ; + +fragment +IdentifierPart + : [a-zA-Z0-9$_] ; + +stringLiteral + : StringLiteralFragment+ ; + +StringLiteralFragment + : '"' DoubleQuotedStringCharacter* '"' + | '\'' SingleQuotedStringCharacter* '\'' ; + +fragment +DoubleQuotedStringCharacter + : ~["\r\n\\] | ('\\' .) ; + +fragment +SingleQuotedStringCharacter + : ~['\r\n\\] | ('\\' .) ; + +VersionLiteral + : [0-9]+ '.' [0-9]+ ('.' [0-9]+)? ; + +WS + : [ \t\r\n\u000C]+ -> skip ; + +COMMENT + : '/*' .*? '*/' -> channel(HIDDEN) ; + +LINE_COMMENT + : '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index a3bd78319..d645a3d44 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -234,7 +234,6 @@ Given the contract: pragma solidity >=0.4.16 <0.7.0; - contract Foo { function bar(bytes3[2] memory) public pure {} function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; } @@ -583,12 +582,11 @@ As an example, the code pragma solidity >=0.4.19 <0.7.0; pragma experimental ABIEncoderV2; - contract Test { struct S { uint a; uint[] b; T[] c; } struct T { uint x; uint y; } - function f(S memory s, T memory t, uint a) public {} - function g() public returns (S memory s, T memory t, uint a) {} + function f(S memory, T memory, uint) public pure {} + function g() public pure returns (S memory, T memory, uint) {} } would result in the JSON: diff --git a/docs/assembly.rst b/docs/assembly.rst index f908c0f57..19e4449e6 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -41,7 +41,7 @@ without a compiler change. .. code:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; library GetCode { function at(address _addr) public view returns (bytes memory o_code) { @@ -136,7 +136,7 @@ Local Solidity variables are available for assignments, for example: .. code:: - pragma solidity >=0.4.11 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract C { uint b; diff --git a/docs/bugs.json b/docs/bugs.json index 66f1808ab..066e17e93 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,12 @@ [ + { + "name": "MemoryArrayCreationOverflow", + "summary": "The creation of very large memory arrays can result in overlapping memory regions and thus memory corruption.", + "description": "No runtime overflow checks were performed for the length of memory arrays during creation. In cases for which the memory size of an array in bytes, i.e. the array length times 32, is larger than 2^256-1, the memory allocation will overflow, potentially resulting in overlapping memory areas. The length of the array is still stored correctly, so copying or iterating over such an array will result in out-of-gas.", + "introduced": "0.2.0", + "fixed": "0.6.5", + "severity": "low" + }, { "name": "YulOptimizerRedundantAssignmentBreakContinue", "summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.", @@ -10,6 +18,14 @@ "yulOptimizer": true } }, + { + "name": "privateCanBeOverridden", + "summary": "Private methods can be overridden by inheriting contracts.", + "description": "While private methods of base contracts are not visible and cannot be called directly from the derived contract, it is still possible to declare a function of the same name and type and thus change the behaviour of the base contract's function.", + "introduced": "0.3.0", + "fixed": "0.5.17", + "severity": "low" + }, { "name": "YulOptimizerRedundantAssignmentBreakContinue0.5", "summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index dc32e2687..b12393016 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -151,6 +151,7 @@ }, "0.2.0": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -171,6 +172,7 @@ }, "0.2.1": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -191,6 +193,7 @@ }, "0.2.2": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -211,6 +214,8 @@ }, "0.3.0": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -232,6 +237,8 @@ }, "0.3.1": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -252,6 +259,8 @@ }, "0.3.2": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -272,6 +281,8 @@ }, "0.3.3": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -291,6 +302,8 @@ }, "0.3.4": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -310,6 +323,8 @@ }, "0.3.5": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -329,6 +344,8 @@ }, "0.3.6": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -346,6 +363,8 @@ }, "0.4.0": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -363,6 +382,8 @@ }, "0.4.1": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -380,6 +401,8 @@ }, "0.4.10": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -395,6 +418,8 @@ }, "0.4.11": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -409,6 +434,8 @@ }, "0.4.12": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -422,6 +449,8 @@ }, "0.4.13": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -435,6 +464,8 @@ }, "0.4.14": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -447,6 +478,8 @@ }, "0.4.15": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -458,6 +491,8 @@ }, "0.4.16": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -471,6 +506,8 @@ }, "0.4.17": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -485,6 +522,8 @@ }, "0.4.18": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -498,6 +537,8 @@ }, "0.4.19": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -512,6 +553,8 @@ }, "0.4.2": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -528,6 +571,8 @@ }, "0.4.20": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -542,6 +587,8 @@ }, "0.4.21": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -556,6 +603,8 @@ }, "0.4.22": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -570,6 +619,8 @@ }, "0.4.23": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -583,6 +634,8 @@ }, "0.4.24": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -596,6 +649,8 @@ }, "0.4.25": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -607,6 +662,8 @@ }, "0.4.26": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2" @@ -615,6 +672,8 @@ }, "0.4.3": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -630,6 +689,8 @@ }, "0.4.4": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -644,6 +705,8 @@ }, "0.4.5": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -660,6 +723,8 @@ }, "0.4.6": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -675,6 +740,8 @@ }, "0.4.7": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -690,6 +757,8 @@ }, "0.4.8": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -705,6 +774,8 @@ }, "0.4.9": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -720,6 +791,8 @@ }, "0.5.0": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -731,6 +804,8 @@ }, "0.5.1": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -742,6 +817,8 @@ }, "0.5.10": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers" ], @@ -749,24 +826,32 @@ }, "0.5.11": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-08-12" }, "0.5.12": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-10-01" }, "0.5.13": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-11-14" }, "0.5.14": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2LoopYulOptimizer" ], @@ -774,16 +859,29 @@ }, "0.5.15": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], "released": "2019-12-17" }, "0.5.16": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden" + ], "released": "2020-01-02" }, + "0.5.17": { + "bugs": [ + "MemoryArrayCreationOverflow" + ], + "released": "2020-03-17" + }, "0.5.2": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -795,6 +893,8 @@ }, "0.5.3": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -806,6 +906,8 @@ }, "0.5.4": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -817,6 +919,8 @@ }, "0.5.5": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", "DynamicConstructorArgumentsClippedABIV2", @@ -830,6 +934,8 @@ }, "0.5.6": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -843,6 +949,8 @@ }, "0.5.7": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -854,6 +962,8 @@ }, "0.5.8": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", @@ -864,6 +974,8 @@ }, "0.5.9": { "bugs": [ + "MemoryArrayCreationOverflow", + "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", @@ -873,24 +985,37 @@ }, "0.6.0": { "bugs": [ + "MemoryArrayCreationOverflow", "YulOptimizerRedundantAssignmentBreakContinue" ], "released": "2019-12-17" }, "0.6.1": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-01-02" }, "0.6.2": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-01-27" }, "0.6.3": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-02-18" }, "0.6.4": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-03-10" + }, + "0.6.5": { + "bugs": [], + "released": "2020-04-06" } } \ No newline at end of file diff --git a/docs/contracts/abstract-contracts.rst b/docs/contracts/abstract-contracts.rst index 51db0dfbe..7b9f83256 100644 --- a/docs/contracts/abstract-contracts.rst +++ b/docs/contracts/abstract-contracts.rst @@ -13,7 +13,7 @@ This can be done by using the ``abstract`` keyword as shown in the following exa defined as abstract, because the function ``utterance()`` was defined, but no implementation was provided (no implementation body ``{ }`` was given).:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; abstract contract Feline { function utterance() public virtual returns (bytes32); diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 33511f020..7a0a7380a 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -1,11 +1,49 @@ .. index:: ! constant -************************ -Constant State Variables -************************ +************************************** +Constant and Immutable State Variables +************************************** -State variables can be declared as ``constant``. In this case, they have to be -assigned from an expression which is a constant at compile time. Any expression +State variables can be declared as ``constant`` or ``immutable``. +In both cases, the variables cannot be modified after the contract has been constructed. +For ``constant`` variables, the value has to be fixed at compile-time, while +for ``immutable``, it can still be assigned at construction time. + +The compiler does not reserve a storage slot for these variables, and every occurrence is +replaced by the respective value. + +Not all types for constants and immutables are implemented at this time. The only supported types are +`strings `_ (only for constants) and `value types `_. + +:: + + pragma solidity >0.6.4 <0.7.0; + + contract C { + uint constant X = 32**22 + 8; + string constant TEXT = "abc"; + bytes32 constant MY_HASH = keccak256("abc"); + uint immutable decimals; + uint immutable maxBalance; + address immutable owner = msg.sender; + + constructor(uint _decimals, address _reference) public { + decimals = _decimals; + // Assignments to immutables can even access the environment. + maxBalance = _reference.balance; + } + + function isBalanceTooHigh(address _other) public view returns (bool) { + return _other.balance > maxBalance; + } + } + + +Constant +======== + +For ``constant`` variables, the value has to be a constant at compile time and it has to be +assigned where the variable is declared. Any expression that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or ``block.number``) or execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions @@ -18,18 +56,17 @@ The reason behind allowing side-effects on the memory allocator is that it should be possible to construct complex objects like e.g. lookup-tables. This feature is not yet fully usable. -The compiler does not reserve a storage slot for these variables, and every occurrence is -replaced by the respective constant expression (which might be computed to a single value by the optimizer). +Immutable +========= -Not all types for constants are implemented at this time. The only supported types are -value types and strings. +Variables declared as ``immutable`` are a bit less restricted than those +declared as ``constant``: Immutable variables can be assigned an arbitrary +value in the constructor of the contract or at the point of their declaration. +They cannot be read during construction time and can only be assigned once. -:: - - pragma solidity >=0.4.0 <0.7.0; - - contract C { - uint constant X = 32**22 + 8; - string constant TEXT = "abc"; - bytes32 constant MY_HASH = keccak256("abc"); - } +The contract creation code generated by the compiler will modify the +contract's runtime code before it is returned by replacing all references +to immutables by the values assigned to the them. This is important if +you are comparing the +runtime code generated by the compiler with the one actually stored in the +blockchain. diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 661663028..8b9e7df64 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it. :: - pragma solidity >0.6.1 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract Test { // This function is called for all messages sent to diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 18c369df4..d3dd0f164 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -154,7 +154,7 @@ A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it explicitly in the final override, but this function will bypass ``Base1.destroy``. The way around this is to use ``super``:: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract owned { constructor() public { owner = msg.sender; } @@ -204,7 +204,7 @@ use the ``override`` keyword in the function header as shown in this example: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base { @@ -227,7 +227,7 @@ bases, it has to explicitly override it: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base1 { @@ -253,7 +253,7 @@ that already overrides all other functions. :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract A { function f() public pure{} } contract B is A {} @@ -293,7 +293,7 @@ of the variable: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract A { @@ -324,7 +324,7 @@ and the ``override`` keyword must be used in the overriding modifier: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base { @@ -342,7 +342,7 @@ explicitly: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Base1 { @@ -498,7 +498,7 @@ One area where inheritance linearization is especially important and perhaps not :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract Base1 { constructor() public {} diff --git a/docs/contracts/interfaces.rst b/docs/contracts/interfaces.rst index 727809460..bce974502 100644 --- a/docs/contracts/interfaces.rst +++ b/docs/contracts/interfaces.rst @@ -22,7 +22,7 @@ Interfaces are denoted by their own keyword: :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; interface Token { enum TokenType { Fungible, NonFungible } @@ -42,7 +42,7 @@ inheritance. :: - pragma solidity >0.6.1 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; interface ParentA { function test() external returns (uint256); diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index 803e53f08..86561ea7b 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -47,12 +47,14 @@ more advanced example to implement a set). :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // We define a new struct datatype that will be used to // hold its data in the calling contract. - struct Data { mapping(uint => bool) flags; } + struct Data { + mapping(uint => bool) flags; + } library Set { // Note that the first parameter is of type "storage @@ -123,7 +125,7 @@ custom types without the overhead of external function calls: :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; struct bigint { uint[] limbs; @@ -237,7 +239,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow :: - pragma solidity >0.5.13 <0.7.0; + pragma solidity >=0.5.14 <0.7.0; library L { function f(uint256) external {} diff --git a/docs/contracts/using-for.rst b/docs/contracts/using-for.rst index 32aa71799..9e63abcf4 100644 --- a/docs/contracts/using-for.rst +++ b/docs/contracts/using-for.rst @@ -29,7 +29,7 @@ may only be used inside a contract, not inside any of its functions. Let us rewrite the set example from the :ref:`libraries` in this way:: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // This is the same code as before, just without comments diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index 3a0905155..5ef105784 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -68,7 +68,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract C { uint private data; @@ -112,7 +112,7 @@ when they are declared. :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract C { uint public data = 42; @@ -151,7 +151,7 @@ to write a function, for example: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract arrayExample { // public state variable diff --git a/docs/control-structures.rst b/docs/control-structures.rst index d5448d0b3..7bbe290ba 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -41,7 +41,7 @@ Internal Function Calls Functions of the current contract can be called directly ("internally"), also recursively, as seen in this nonsensical example:: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract C { function g(uint a) public pure returns (uint ret) { return a + f(); } @@ -82,7 +82,7 @@ to the total balance of that contract: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract InfoFeed { function info() public payable returns (uint ret) { return 42; } @@ -160,7 +160,7 @@ Those parameters will still be present on the stack, but they are inaccessible. :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract C { // omitted name for parameter @@ -183,7 +183,7 @@ is compiled so recursive creation-dependencies are not possible. :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract D { uint public x; @@ -238,7 +238,7 @@ which only need to be created if there is a dispute. :: - pragma solidity >0.6.1 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; contract D { uint public x; @@ -307,7 +307,7 @@ groupings of expressions. :: - pragma solidity >0.4.23 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract C { uint index; @@ -352,7 +352,7 @@ because only a reference and not a copy is passed. :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract C { uint[20] x; diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index 92a755973..f2034272a 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -24,7 +24,7 @@ to receive their money - contracts cannot activate themselves. :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract SimpleAuction { // Parameters of the auction. Times are either @@ -184,7 +184,7 @@ invalid bids. :: - pragma solidity >0.4.23 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract BlindAuction { struct Bid { diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index fe310f37a..cd4084c84 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -338,7 +338,7 @@ The full contract :: - pragma solidity >=0.4.24 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract SimplePaymentChannel { address payable public sender; // The account sending payments. diff --git a/docs/examples/modular.rst b/docs/examples/modular.rst index a3d932b09..a95a675c6 100644 --- a/docs/examples/modular.rst +++ b/docs/examples/modular.rst @@ -19,7 +19,7 @@ and the sum of all balances is an invariant across the lifetime of the contract. :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; library Balances { function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst index d79c6526e..caafaa7ed 100644 --- a/docs/examples/safe-remote.rst +++ b/docs/examples/safe-remote.rst @@ -25,7 +25,7 @@ you can use state machine-like constructs inside a contract. :: - pragma solidity >=0.4.22 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract Purchase { uint public value; diff --git a/docs/grammar.txt b/docs/grammar.txt deleted file mode 100644 index b8b2893a2..000000000 --- a/docs/grammar.txt +++ /dev/null @@ -1,193 +0,0 @@ -SourceUnit = (PragmaDirective | ImportDirective | ContractDefinition)* - -// Pragma actually parses anything up to the trailing ';' to be fully forward-compatible. -PragmaDirective = 'pragma' Identifier ([^;]+) ';' - -ImportDirective = 'import' StringLiteral ('as' Identifier)? ';' - | 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';' - | 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';' - -ContractDefinition = 'abstract'? ( 'contract' | 'library' | 'interface' ) Identifier - ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? - '{' ContractPart* '}' - -ContractPart = StateVariableDeclaration | UsingForDeclaration - | StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition - -InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )? - -StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' | OverrideSpecifier )* Identifier ('=' Expression)? ';' -UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' -StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration ';' (VariableDeclaration ';')* ) '}' - -ModifierDefinition = 'modifier' Identifier ParameterList? ( 'virtual' | OverrideSpecifier )* Block -ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? - -FunctionDefinition = FunctionDescriptor ParameterList - ( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' | 'virtual' | OverrideSpecifier )* - ( 'returns' ParameterList )? ( ';' | Block ) - -FunctionDescriptor = 'function' Identifier | 'constructor' | 'fallback' | 'receive' - -OverrideSpecifier = 'override' ( '(' UserDefinedTypeName (',' UserDefinedTypeName)* ')' )? - -EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';' - -EnumValue = Identifier -EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}' - -ParameterList = '(' ( Parameter (',' Parameter)* )? ')' -Parameter = TypeName StorageLocation? Identifier? - -EventParameterList = '(' ( EventParameter (',' EventParameter )* )? ')' -EventParameter = TypeName 'indexed'? Identifier? - -FunctionTypeParameterList = '(' ( FunctionTypeParameter (',' FunctionTypeParameter )* )? ')' -FunctionTypeParameter = TypeName StorageLocation? - -// semantic restriction: mappings and structs (recursively) containing mappings -// are not allowed in argument lists -VariableDeclaration = TypeName StorageLocation? Identifier - -TypeName = ElementaryTypeName - | UserDefinedTypeName - | Mapping - | ArrayTypeName - | FunctionTypeName - | ( 'address' 'payable' ) - -UserDefinedTypeName = Identifier ( '.' Identifier )* - -Mapping = 'mapping' '(' ( ElementaryTypeName | UserDefinedTypeName ) '=>' TypeName ')' -ArrayTypeName = TypeName '[' Expression? ']' -FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* - ( 'returns' FunctionTypeParameterList )? -StorageLocation = 'memory' | 'storage' | 'calldata' -StateMutability = 'pure' | 'view' | 'payable' - -Block = '{' Statement* '}' -Statement = IfStatement | TryStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | - ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | EmitStatement | SimpleStatement ) ';' - -ExpressionStatement = Expression -IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? -TryStatement = 'try' Expression ( 'returns' ParameterList )? Block CatchClause+ -CatchClause = 'catch' ( Identifier? ParameterList )? Block -WhileStatement = 'while' '(' Expression ')' Statement -PlaceholderStatement = '_' -SimpleStatement = VariableDefinition | ExpressionStatement -ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -InlineAssemblyStatement = 'assembly' StringLiteral? AssemblyBlock -DoWhileStatement = 'do' Statement 'while' '(' Expression ')' -Continue = 'continue' -Break = 'break' -Return = 'return' Expression? -Throw = 'throw' -EmitStatement = 'emit' FunctionCall -VariableDefinition = (VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )? - -// Precedence by order (see github.com/ethereum/solidity/pull/732) -Expression - = Expression ('++' | '--') - | NewExpression - | IndexAccess - | IndexRangeAccess - | MemberAccess - | FunctionCall - | Expression '{' NameValueList '}' - | '(' Expression ')' - | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression - | Expression '**' Expression - | Expression ('*' | '/' | '%') Expression - | Expression ('+' | '-') Expression - | Expression ('<<' | '>>') Expression - | Expression '&' Expression - | Expression '^' Expression - | Expression '|' Expression - | Expression ('<' | '>' | '<=' | '>=') Expression - | Expression ('==' | '!=') Expression - | Expression '&&' Expression - | Expression '||' Expression - | Expression '?' Expression ':' Expression - | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression - | PrimaryExpression - -PrimaryExpression = BooleanLiteral - | NumberLiteral - | HexLiteral - | StringLiteral - | TupleExpression - | Identifier - | ElementaryTypeNameExpression - -ExpressionList = Expression ( ',' Expression )* -NameValueList = Identifier ':' Expression ( ',' Identifier ':' Expression )* - -FunctionCall = Expression '(' FunctionCallArguments ')' -FunctionCallArguments = '{' NameValueList? '}' - | ExpressionList? - -NewExpression = 'new' TypeName -MemberAccess = Expression '.' Identifier -IndexAccess = Expression '[' Expression? ']' -IndexRangeAccess = Expression '[' Expression? ':' Expression? ']' - -BooleanLiteral = 'true' | 'false' -NumberLiteral = ( HexNumber | DecimalNumber ) (' ' NumberUnit)? -NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' -HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') -StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' -Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]* - -HexNumber = '0x' [0-9a-fA-F]+ -DecimalNumber = [0-9]+ ( '.' [0-9]* )? ( [eE] [0-9]+ )? - -TupleExpression = '(' ( Expression? ( ',' Expression? )* )? ')' - | '[' ( Expression ( ',' Expression )* )? ']' - -ElementaryTypeNameExpression = ElementaryTypeName - -ElementaryTypeName = 'address' | 'bool' | 'string' | Int | Uint | Byte | Fixed | Ufixed - -Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' - -Uint = 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' - -Byte = 'byte' | 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' - -Fixed = 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) - -Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) - - -AssemblyBlock = '{' AssemblyStatement* '}' - -AssemblyStatement = AssemblyBlock - | AssemblyFunctionDefinition - | AssemblyVariableDeclaration - | AssemblyAssignment - | AssemblyIf - | AssemblyExpression - | AssemblySwitch - | AssemblyForLoop - | AssemblyBreakContinue - | AssemblyLeave -AssemblyFunctionDefinition = - 'function' Identifier '(' AssemblyIdentifierList? ')' - ( '->' AssemblyIdentifierList )? AssemblyBlock -AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpression )? -AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression -AssemblyExpression = AssemblyFunctionCall | Identifier | Literal -AssemblyIf = 'if' AssemblyExpression AssemblyBlock -AssemblySwitch = 'switch' AssemblyExpression ( AssemblyCase+ AssemblyDefault? | AssemblyDefault ) -AssemblyCase = 'case' Literal AssemblyBlock -AssemblyDefault = 'default' AssemblyBlock -AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock -AssemblyBreakContinue = 'break' | 'continue' -AssemblyLeave = 'leave' -AssemblyFunctionCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')' - -AssemblyIdentifierList = Identifier ( ',' Identifier )* diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index fada5d291..a5f056493 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -17,7 +17,7 @@ Storage Example :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; contract SimpleStorage { uint storedData; diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 1fec22aec..a2629319b 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -284,7 +284,7 @@ for the two function parameters and two return variables. :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.21 <0.7.0; /** @title Shape calculator. */ contract ShapeCalculator { diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 085bf99e7..3c30fec86 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -788,6 +788,7 @@ Modifiers - ``view`` for functions: Disallows modification of state. - ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. +- ``immutable`` for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. - ``virtual`` for functions and modifiers: Allows the function's or modifier's @@ -809,5 +810,5 @@ These keywords are reserved in Solidity. They might become part of the syntax in Language Grammar ================ -.. literalinclude:: grammar.txt - :language: none +.. literalinclude:: Solidity.g4 + :language: antlr diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index ebf54b3a4..b47370493 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -81,7 +81,7 @@ as it uses ``call`` which forwards all remaining gas by default: :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.2 <0.7.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -277,7 +277,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The :: - pragma solidity >=0.5.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Map { mapping (uint => uint)[] array; @@ -547,6 +547,7 @@ not mean loss of proving power. pragma solidity >=0.5.0; pragma experimental SMTChecker; + // This may report a warning if no SMT solver available. contract Recover { @@ -601,6 +602,7 @@ types. pragma solidity >=0.5.0; pragma experimental SMTChecker; // This will report a warning + contract Aliasing { uint[] array; diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 11ddaef07..c637c8379 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -109,7 +109,7 @@ Yes:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; abstract contract A { function spam() virtual pure public; @@ -326,7 +326,7 @@ Yes:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity ^0.6.0; contract A { @@ -745,7 +745,7 @@ manner as modifiers if the function declaration is long or hard to read. Yes:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // Base contracts just to make this compile contract B { @@ -777,7 +777,7 @@ Yes:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // Base contracts just to make this compile @@ -1000,7 +1000,7 @@ As shown in the example below, if the contract name is `Congress` and the librar Yes:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // Owned.sol @@ -1034,7 +1034,7 @@ and in ``Congress.sol``:: No:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; // owned.sol @@ -1138,7 +1138,7 @@ multiline comment starting with `/**` and ending with `*/`. For example, the contract from `a simple smart contract `_ with the comments added looks like the one below:: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.16 <0.7.0; /// @author The Solidity Team diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index acb48fb8a..f69d25b0f 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -66,7 +66,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.4.22 <0.7.0; contract MappingExample { @@ -120,7 +120,7 @@ the ``sum`` function iterates over to sum all the values. :: - pragma solidity >=0.5.99 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; struct IndexValue { uint keyIndex; uint value; } struct KeyFlag { uint key; bool deleted; } diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 15cd110dd..f7aca7190 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -57,7 +57,7 @@ Data locations are not only relevant for persistency of data, but also for the s :: - pragma solidity >=0.4.0 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract C { // The data location of x is storage. @@ -124,6 +124,12 @@ Accessing an array past its end causes a failing assertion. Methods ``.push()`` to append a new element at the end of the array, where ``.push()`` appends a zero-initialized element and returns a reference to it. +.. index:: ! string, ! bytes + +.. _strings: + +.. _bytes: + ``bytes`` and ``strings`` as Arrays ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +274,7 @@ Array Members :: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract ArrayContract { uint[2**20] m_aLotOfIntegers; @@ -381,7 +387,7 @@ If ``start`` is greater than ``end`` or if ``end`` is greater than the length of the array, an exception is thrown. Both ``start`` and ``end`` are optional: ``start`` defaults - to ``0`` and ``end`` defaults to the length of the array. +to ``0`` and ``end`` defaults to the length of the array. Array slices do not have any members. They are implicitly convertible to arrays of their underlying type @@ -400,7 +406,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete :: - pragma solidity >=0.4.99 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; contract Proxy { /// Address of the client contract managed by proxy i.e., this contract @@ -437,7 +443,7 @@ shown in the following example: :: - pragma solidity >=0.4.11 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // Defines a new type with two fields. // Declaring a struct outside of a contract allows @@ -494,7 +500,7 @@ shown in the following example: The contract does not provide the full functionality of a crowdfunding contract, but it contains the basic concepts necessary to understand structs. -Struct types can be used inside mappings and arrays and they can itself +Struct types can be used inside mappings and arrays and they can themselves contain mappings and arrays. It is not possible for a struct to contain a member of its own type, diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 8984a5b8e..80b9d548c 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -300,6 +300,11 @@ All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-l The ``gas`` option is available on all three methods, while the ``value`` option is not supported for ``delegatecall``. +.. note:: + It is best to avoid relying on hardcoded gas values in your smart contract code, + regardless of whether state is read from or written to, as this can have many pitfalls. + Also, access to gas might change in the future. + .. note:: All contracts can be converted to ``address`` type, so it is possible to query the balance of the current contract using ``address(this).balance``. @@ -645,7 +650,7 @@ External (or public) functions have the following members: Example that shows how to use the members:: - pragma solidity >=0.4.16 <0.7.0; + pragma solidity >=0.6.0 <0.7.0; // This will report a warning contract Example { @@ -665,7 +670,6 @@ Example that shows how to use internal function types:: pragma solidity >=0.4.16 <0.7.0; - library ArrayUtils { // internal functions can be used in internal library functions because // they will be part of the same code context diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index eb4ddf0c6..02218592e 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -302,7 +302,8 @@ Input Description // evm.bytecode.opcodes - Opcodes list // evm.bytecode.sourceMap - Source mapping (useful for debugging) // evm.bytecode.linkReferences - Link references (if unlinked object) - // evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode) + // evm.deployedBytecode* - Deployed bytecode (has all the options that evm.bytecode has) + // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.methodIdentifiers - The list of function hashes // evm.gasEstimates - Function gas estimates // ewasm.wast - eWASM S-expressions format (not supported at the moment) @@ -424,8 +425,14 @@ Output Description } } }, - // The same layout as above. - "deployedBytecode": { }, + "deployedBytecode": { + ..., // The same layout as above. + "immutableReferences": [ + // There are two references to the immutable with AST ID 3, both 32 bytes long. One is + // at bytecode offset 42, the other at bytecode offset 80. + "3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }] + ] + }, // The list of function hashes "methodIdentifiers": { "delegate(address)": "5c19a95c" @@ -605,8 +612,8 @@ Assume you have the following contracts you want to update declared in ``Source. .. code-block:: none - // This will not compile - pragma solidity >0.4.23; + // This will not compile after 0.5.0 + pragma solidity >0.4.23 <0.5.0; contract Updateable { function run() public view returns (bool); @@ -687,7 +694,7 @@ The command above applies all changes as shown below. Please review them careful .. code-block:: none - pragma solidity >0.4.23; + pragma solidity >=0.6.0 <0.7.0; abstract contract Updateable { function run() public view virtual returns (bool); diff --git a/docs/yul.rst b/docs/yul.rst index 93bfe48a9..c6fb28986 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -111,7 +111,7 @@ Stand-Alone Usage ================= You can use Yul in its stand-alone form in the EVM dialect using the Solidity compiler. -This will use the `Yul object notation `_ so that it is possible to refer +This will use the :ref:`Yul object notation ` so that it is possible to refer to code as data to deploy contracts. This Yul mode is available for the commandline compiler (use ``--strict-assembly``) and for the :ref:`standard-json interface `: @@ -146,7 +146,7 @@ so you can e.g. use ``//`` and ``/* */`` to denote comments. There is one exception: Identifiers in Yul can contain dots: ``.``. Yul can specify "objects" that consist of code, data and sub-objects. -Please see `Yul Objects `_ below for details on that. +Please see :ref:`Yul Objects ` below for details on that. In this section, we are only concerned with the code part of such an object. This code part always consists of a curly-braces delimited block. Most tools support specifying just a code block diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 7ed44d236..fa6ea4f16 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -283,6 +283,24 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end) ); break; + case PushImmutable: + collection.append(createJsonValue( + "PUSHIMMUTABLE", + sourceIndex, + i.location().start, + i.location().end, + m_immutables.at(h256(i.data())) + )); + break; + case AssignImmutable: + collection.append(createJsonValue( + "ASSIGNIMMUTABLE", + sourceIndex, + i.location().start, + i.location().end, + m_immutables.at(h256(i.data())) + )); + break; case Tag: collection.append( createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data()))); @@ -333,6 +351,20 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) return AssemblyItem{PushLibraryAddress, h}; } +AssemblyItem Assembly::newPushImmutable(string const& _identifier) +{ + h256 h(util::keccak256(_identifier)); + m_immutables[h] = _identifier; + return AssemblyItem{PushImmutable, h}; +} + +AssemblyItem Assembly::newImmutableAssignment(string const& _identifier) +{ + h256 h(util::keccak256(_identifier)); + m_immutables[h] = _identifier; + return AssemblyItem{AssignImmutable, h}; +} + Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; @@ -495,16 +527,44 @@ LinkerObject const& Assembly::assemble() const // Otherwise ensure the object is actually clear. assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references."); + LinkerObject& ret = m_assembledObject; + size_t subTagSize = 1; + map>> immutableReferencesBySub; for (auto const& sub: m_subs) { - sub->assemble(); + auto const& linkerObject = sub->assemble(); + if (!linkerObject.immutableReferences.empty()) + { + assertThrow( + immutableReferencesBySub.empty(), + AssemblyException, + "More than one sub-assembly references immutables." + ); + immutableReferencesBySub = linkerObject.immutableReferences; + } for (size_t tagPos: sub->m_tagPositionsInBytecode) if (tagPos != size_t(-1) && tagPos > subTagSize) subTagSize = tagPos; } - LinkerObject& ret = m_assembledObject; + bool setsImmutables = false; + bool pushesImmutables = false; + + for (auto const& i: m_items) + if (i.type() == AssignImmutable) + { + i.setImmutableOccurrences(immutableReferencesBySub[i.data()].second.size()); + setsImmutables = true; + } + else if (i.type() == PushImmutable) + pushesImmutables = true; + if (setsImmutables || pushesImmutables) + assertThrow( + setsImmutables != pushesImmutables, + AssemblyException, + "Cannot push and assign immutables in the same assembly subroutine." + ); size_t bytesRequiredForCode = bytesRequired(subTagSize); m_tagPositionsInBytecode = vector(m_usedTags, -1); @@ -598,6 +658,25 @@ LinkerObject const& Assembly::assemble() const ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data()); ret.bytecode.resize(ret.bytecode.size() + 20); break; + case PushImmutable: + ret.bytecode.push_back(uint8_t(Instruction::PUSH32)); + ret.immutableReferences[i.data()].first = m_immutables.at(i.data()); + ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size()); + ret.bytecode.resize(ret.bytecode.size() + 32); + break; + case AssignImmutable: + for (auto const& offset: immutableReferencesBySub[i.data()].second) + { + ret.bytecode.push_back(uint8_t(Instruction::DUP1)); + // TODO: should we make use of the constant optimizer methods for pushing the offsets? + bytes offsetBytes = toCompactBigEndian(u256(offset)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH1) - 1 + offsetBytes.size()); + ret.bytecode += offsetBytes; + ret.bytecode.push_back(uint8_t(Instruction::MSTORE)); + } + immutableReferencesBySub.erase(i.data()); + ret.bytecode.push_back(uint8_t(Instruction::POP)); + break; case PushDeployTimeAddress: ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.bytecode.resize(ret.bytecode.size() + 20); @@ -615,6 +694,13 @@ LinkerObject const& Assembly::assemble() const } } + assertThrow( + immutableReferencesBySub.empty(), + AssemblyException, + "Some immutables were read from but never assigned." + ); + + if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) // Append an INVALID here to help tests find miscompilation. ret.bytecode.push_back(uint8_t(Instruction::INVALID)); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index a76538375..e9e3630a8 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -54,6 +54,8 @@ public: Assembly& sub(size_t _sub) { return *m_subs.at(_sub); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushLibraryAddress(std::string const& _identifier); + AssemblyItem newPushImmutable(std::string const& _identifier); + AssemblyItem newImmutableAssignment(std::string const& _identifier); AssemblyItem const& append(AssemblyItem const& _i); AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } @@ -64,6 +66,8 @@ public: /// after compilation and CODESIZE is not an option. void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); } + void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); } + void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } @@ -166,6 +170,7 @@ protected: std::vector> m_subs; std::map m_strings; std::map m_libraries; ///< Identifiers of libraries to be linked. + std::map m_immutables; ///< Identifiers of immutables. mutable LinkerObject m_assembledObject; mutable std::vector m_tagPositionsInBytecode; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index c238a3a3c..610c6f5dd 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -80,6 +80,13 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const case PushLibraryAddress: case PushDeployTimeAddress: return 1 + 20; + case PushImmutable: + return 1 + 32; + case AssignImmutable: + if (m_immutableOccurrences) + return 1 + (3 + 32) * *m_immutableOccurrences; + else + return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways. default: break; } @@ -90,6 +97,8 @@ int AssemblyItem::arguments() const { if (type() == Operation) return instructionInfo(instruction()).args; + else if (type() == AssignImmutable) + return 1; else return 0; } @@ -108,6 +117,7 @@ int AssemblyItem::returnValues() const case PushSubSize: case PushProgramSize: case PushLibraryAddress: + case PushImmutable: case PushDeployTimeAddress: return 1; case Tag: @@ -135,6 +145,7 @@ bool AssemblyItem::canBeFunctional() const case PushProgramSize: case PushLibraryAddress: case PushDeployTimeAddress: + case PushImmutable: return true; case Tag: return false; @@ -210,6 +221,12 @@ string AssemblyItem::toAssemblyText() const case PushDeployTimeAddress: text = string("deployTimeAddress()"); break; + case PushImmutable: + text = string("immutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + break; + case AssignImmutable: + text = string("assignImmutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + break; case UndefinedItem: assertThrow(false, AssemblyException, "Invalid assembly item."); break; @@ -275,6 +292,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item) case PushDeployTimeAddress: _out << " PushDeployTimeAddress"; break; + case PushImmutable: + _out << " PushImmutable"; + break; + case AssignImmutable: + _out << " AssignImmutable"; + break; case UndefinedItem: _out << " ???"; break; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index e506a9fdb..fc8c63c67 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -44,7 +44,9 @@ enum AssemblyItemType { Tag, PushData, PushLibraryAddress, ///< Push a currently unknown address of another (library) contract. - PushDeployTimeAddress ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. + PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. + PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor. + AssignImmutable ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code. }; class Assembly; @@ -153,6 +155,8 @@ public: size_t m_modifierDepth = 0; + void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = std::make_shared(_n); } + private: AssemblyItemType m_type; Instruction m_instruction; ///< Only valid if m_type == Operation @@ -162,6 +166,8 @@ private: /// Pushed value for operations with data to be determined during assembly stage, /// e.g. PushSubSize, PushTag, PushSub, etc. mutable std::shared_ptr m_pushedValue; + /// Number of PushImmutable's with the same hash. Only used for AssignImmutable. + mutable std::shared_ptr m_immutableOccurrences; }; inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 7e447a9a1..5f1ca736c 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -91,6 +91,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool { // can be ignored } + else if (_item.type() == AssignImmutable) + // Since AssignImmutable breaks blocks, it should be fine to only consider its changes to the stack, which + // is the same as POP. + return feedItem(AssemblyItem(Instruction::POP), _copyItem); else if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index e7a1e9667..ab0e26507 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -40,6 +40,10 @@ struct LinkerObject /// need to be replaced by the actual addresses by the linker. std::map linkReferences; + /// Map from hashes of the identifiers of immutable variables to the full identifier of the immutable and + /// to a list of offsets into the bytecode that refer to their values. + std::map>> immutableReferences; + /// Appends the bytecode of @a _other and incorporates its link references. void append(LinkerObject const& _other); diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 4ea9a01ae..76eeb5956 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -36,6 +36,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case UndefinedItem: case Tag: case PushDeployTimeAddress: + case AssignImmutable: return true; case Push: case PushString: @@ -45,6 +46,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case PushProgramSize: case PushData: case PushLibraryAddress: + case PushImmutable: return false; case Operation: { diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index bb54c836f..c7a517a0b 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -14,6 +14,8 @@ set(sources analysis/DeclarationContainer.h analysis/DocStringAnalyser.cpp analysis/DocStringAnalyser.h + analysis/ImmutableValidator.cpp + analysis/ImmutableValidator.h analysis/GlobalContext.cpp analysis/GlobalContext.h analysis/NameAndTypeResolver.cpp @@ -100,6 +102,8 @@ set(sources formal/SMTPortfolio.cpp formal/SMTPortfolio.h formal/SolverInterface.h + formal/Sorts.cpp + formal/Sorts.h formal/SSAVariable.cpp formal/SSAVariable.h formal/SymbolicTypes.cpp diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index e802c4204..ee0a39163 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -153,7 +153,7 @@ void DocStringAnalyser::parseDocStrings( appendError( _node.documentation()->location(), "Documentation tag \"@" + docTag.first + " " + docTag.second.content + "\"" + - " exceedes the number of return parameters." + " exceeds the number of return parameters." ); else { diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp new file mode 100644 index 000000000..6e0d0f6c7 --- /dev/null +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -0,0 +1,220 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +using namespace solidity::frontend; + +void ImmutableValidator::analyze() +{ + m_inConstructionContext = true; + + auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | boost::adaptors::reversed; + + for (ContractDefinition const* contract: linearizedContracts) + for (VariableDeclaration const* stateVar: contract->stateVariables()) + if (stateVar->value()) + { + stateVar->value()->accept(*this); + solAssert(m_initializedStateVariables.emplace(stateVar).second, ""); + } + + for (ContractDefinition const* contract: linearizedContracts) + if (contract->constructor()) + visitCallableIfNew(*contract->constructor()); + + for (ContractDefinition const* contract: linearizedContracts) + for (std::shared_ptr const inheritSpec: contract->baseContracts()) + if (auto args = inheritSpec->arguments()) + ASTNode::listAccept(*args, *this); + + m_inConstructionContext = false; + + for (ContractDefinition const* contract: linearizedContracts) + { + for (auto funcDef: contract->definedFunctions()) + visitCallableIfNew(*funcDef); + + for (auto modDef: contract->functionModifiers()) + visitCallableIfNew(*modDef); + } + + checkAllVariablesInitialized(m_currentContract.location()); +} + +bool ImmutableValidator::visit(FunctionDefinition const& _functionDefinition) +{ + return analyseCallable(_functionDefinition); +} + +bool ImmutableValidator::visit(ModifierDefinition const& _modifierDefinition) +{ + return analyseCallable(_modifierDefinition); +} + +bool ImmutableValidator::visit(MemberAccess const& _memberAccess) +{ + _memberAccess.expression().accept(*this); + + if (auto contractType = dynamic_cast(_memberAccess.expression().annotation().type)) + if (!contractType->isSuper()) + // external access, no analysis needed. + return false; + + if (auto varDecl = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + analyseVariableReference(*varDecl, _memberAccess); + else if (auto funcType = dynamic_cast(_memberAccess.annotation().type)) + if (funcType->kind() == FunctionType::Kind::Internal && funcType->hasDeclaration()) + visitCallableIfNew(funcType->declaration()); + + return false; +} + +bool ImmutableValidator::visit(IfStatement const& _ifStatement) +{ + bool prevInBranch = m_inBranch; + + _ifStatement.condition().accept(*this); + + m_inBranch = true; + _ifStatement.trueStatement().accept(*this); + + if (auto falseStatement = _ifStatement.falseStatement()) + falseStatement->accept(*this); + + m_inBranch = prevInBranch; + + return false; +} + +bool ImmutableValidator::visit(WhileStatement const& _whileStatement) +{ + bool prevInLoop = m_inLoop; + m_inLoop = true; + + _whileStatement.condition().accept(*this); + _whileStatement.body().accept(*this); + + m_inLoop = prevInLoop; + + return false; +} + +void ImmutableValidator::endVisit(Identifier const& _identifier) +{ + if (auto const callableDef = dynamic_cast(_identifier.annotation().referencedDeclaration)) + visitCallableIfNew(callableDef->resolveVirtual(m_currentContract)); + if (auto const varDecl = dynamic_cast(_identifier.annotation().referencedDeclaration)) + analyseVariableReference(*varDecl, _identifier); +} + +void ImmutableValidator::endVisit(Return const& _return) +{ + if (m_currentConstructor != nullptr) + checkAllVariablesInitialized(_return.location()); +} + +bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration) +{ + FunctionDefinition const* prevConstructor = m_currentConstructor; + m_currentConstructor = nullptr; + + if (FunctionDefinition const* funcDef = dynamic_cast(&_callableDeclaration)) + { + ASTNode::listAccept(funcDef->modifiers(), *this); + + if (funcDef->isConstructor()) + m_currentConstructor = funcDef; + + if (funcDef->isImplemented()) + funcDef->body().accept(*this); + } + else if (ModifierDefinition const* modDef = dynamic_cast(&_callableDeclaration)) + modDef->body().accept(*this); + + m_currentConstructor = prevConstructor; + + return false; +} + +void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression) +{ + if (!_variableReference.isStateVariable() || !_variableReference.immutable()) + return; + + if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment) + { + if (!m_currentConstructor) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables can only be initialized inline or assigned directly in the constructor." + ); + else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables must be initialized in the constructor of the contract they are defined in." + ); + else if (m_inLoop) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables can only be initialized once, not in a while statement." + ); + else if (m_inBranch) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables must be initialized unconditionally, not in an if statement." + ); + + if (!m_initializedStateVariables.emplace(&_variableReference).second) + m_errorReporter.typeError( + _expression.location(), + "Immutable state variable already initialized." + ); + } + else if (m_inConstructionContext) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables cannot be read during contract creation time, which means " + "they cannot be read in the constructor or any function or modifier called from it." + ); +} + +void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location) +{ + for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts) + for (VariableDeclaration const* varDecl: contract->stateVariables()) + if (varDecl->immutable()) + if (!util::contains(m_initializedStateVariables, varDecl)) + m_errorReporter.typeError( + _location, + solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()), + "Construction control flow ends without initializing all immutable state variables." + ); +} + +void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration) +{ + CallableDeclaration const* _callable = dynamic_cast(&_declaration); + solAssert(_callable != nullptr, ""); + + if (m_visitedCallables.emplace(_callable).second) + _declaration.accept(*this); +} diff --git a/libsolidity/analysis/ImmutableValidator.h b/libsolidity/analysis/ImmutableValidator.h new file mode 100644 index 000000000..5845ba011 --- /dev/null +++ b/libsolidity/analysis/ImmutableValidator.h @@ -0,0 +1,78 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include + +#include + +namespace solidity::frontend +{ + +/** + * Validates access and initialization of immutable variables: + * must be directly initialized in their respective c'tor + * can not be read by any function/modifier called by the c'tor (or the c'tor itself) + * must be initialized outside loops (only one initialization) + * must be initialized outside ifs (must be initialized unconditionally) + * must be initialized exactly once (no multiple statements) + * must be initialized exactly once (no early return to skip initialization) +*/ +class ImmutableValidator: private ASTConstVisitor +{ + using CallableDeclarationSet = std::set; + +public: + ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition): + m_currentContract(_contractDefinition), + m_errorReporter(_errorReporter) + { } + + void analyze(); + +private: + bool visit(FunctionDefinition const& _functionDefinition); + bool visit(ModifierDefinition const& _modifierDefinition); + bool visit(MemberAccess const& _memberAccess); + bool visit(IfStatement const& _ifStatement); + bool visit(WhileStatement const& _whileStatement); + void endVisit(Identifier const& _identifier); + void endVisit(Return const& _return); + + bool analyseCallable(CallableDeclaration const& _callableDeclaration); + void analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression); + + void checkAllVariablesInitialized(langutil::SourceLocation const& _location); + + void visitCallableIfNew(Declaration const& _declaration); + + ContractDefinition const& m_currentContract; + + CallableDeclarationSet m_visitedCallables; + + std::set m_initializedStateVariables; + langutil::ErrorReporter& m_errorReporter; + + FunctionDefinition const* m_currentConstructor = nullptr; + bool m_inLoop = false; + bool m_inBranch = false; + bool m_inConstructionContext = false; +}; + +} diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 6867f049c..94a16d6f1 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -204,7 +204,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) else { _typeName.annotation().type = TypeProvider::emptyTuple(); - typeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); + fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); } } @@ -328,6 +328,8 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) if (_variable.isConstant() && !_variable.isStateVariable()) m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables."); + if (_variable.immutable() && !_variable.isStateVariable()) + m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables."); if (!_variable.typeName()) { @@ -394,7 +396,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) else if (_variable.isStateVariable()) { solAssert(varLoc == Location::Unspecified, ""); - typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage; + typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage; } else if ( dynamic_cast(_variable.scope()) || diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 74fa8d458..ba973bffa 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -303,7 +303,9 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) ); } - if (!_function.isImplemented() && !_function.modifiers().empty()) + if (m_isInterface && !_function.modifiers().empty()) + m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers."); + else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers."); return true; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 5cf003bed..2bdc706a2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -480,6 +480,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) "Initial value for constant variable has to be compile-time constant." ); } + else if (_variable.immutable()) + { + if (!_variable.type()->isValueType()) + m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type."); + if ( + auto const* functionType = dynamic_cast(_variable.type()); + functionType && functionType->kind() == FunctionType::Kind::External + ) + m_errorReporter.typeError(_variable.location(), "Immutable variables of external function type are not yet supported."); + solAssert(_variable.type()->sizeOnStack() == 1 || m_errorReporter.hasErrors(), ""); + } + if (!_variable.isStateVariable()) { if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) @@ -641,16 +653,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); + if (var->immutable()) + { + m_errorReporter.typeError(_identifier.location, "Assembly access to immutable variables is not supported."); + return size_t(-1); + } if (var->isConstant()) { - var = rootVariableDeclaration(*var); + var = rootConstVariableDeclaration(*var); - if (!var->value()) + if (var && !var->value()) { m_errorReporter.typeError(_identifier.location, "Constant has no value."); return size_t(-1); } - else if (!type(*var)->isValueType() || ( + else if (!var || !type(*var)->isValueType() || ( dynamic_cast(var->value().get()) == nullptr && type(*var->value())->category() != Type::Category::RationalNumber )) @@ -731,6 +748,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast(declaration)) { + m_errorReporter.declarationError(_identifier.location, "Access to functions is not allowed in inline assembly."); + return size_t(-1); } else if (dynamic_cast(declaration)) { @@ -1052,17 +1071,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (!varDecl.annotation().type) m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); - if (auto ref = dynamic_cast(type(varDecl))) - { - if (ref->dataStoredIn(DataLocation::Storage)) - { - string errorText{"Uninitialized storage pointer."}; - solAssert(varDecl.referenceLocation() != VariableDeclaration::Location::Unspecified, "Expected a specified location at this point"); - solAssert(m_scope, ""); - m_errorReporter.declarationError(varDecl.location(), errorText); - } - } - else if (dynamic_cast(type(varDecl))) + if (dynamic_cast(type(varDecl))) m_errorReporter.typeError( varDecl.location(), "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." @@ -1308,11 +1317,11 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& if (auto const* tupleExpression = dynamic_cast(&_expression)) { auto const* tupleType = dynamic_cast(&_type); - auto const& types = tupleType ? tupleType->components() : vector { &_type }; + auto const& types = tupleType && tupleExpression->components().size() > 1 ? tupleType->components() : vector { &_type }; solAssert( tupleExpression->components().size() == types.size() || m_errorReporter.hasErrors(), - "Array sizes don't match or no errors generated." + "Array sizes don't match and no errors generated." ); for (size_t i = 0; i < min(tupleExpression->components().size(), types.size()); i++) @@ -1336,7 +1345,10 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& bool TypeChecker::visit(Assignment const& _assignment) { - requireLValue(_assignment.leftHandSide()); + requireLValue( + _assignment.leftHandSide(), + _assignment.assignmentOperator() == Token::Assign + ); TypePointer t = type(_assignment.leftHandSide()); _assignment.annotation().type = t; @@ -1394,7 +1406,10 @@ bool TypeChecker::visit(TupleExpression const& _tuple) for (auto const& component: components) if (component) { - requireLValue(*component); + requireLValue( + *component, + _tuple.annotation().lValueOfOrdinaryAssignment + ); types.push_back(type(*component)); } else @@ -1479,7 +1494,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation) Token op = _operation.getOperator(); bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete); if (modifying) - requireLValue(_operation.subExpression()); + requireLValue(_operation.subExpression(), false); else _operation.subExpression().accept(*this); TypePointer const& subExprType = type(_operation.subExpression()); @@ -2987,9 +3002,10 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte return true; } -void TypeChecker::requireLValue(Expression const& _expression) +void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment) { _expression.annotation().lValueRequested = true; + _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; _expression.accept(*this); if (_expression.annotation().isLValue) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index d428a6ac9..a26ab81bd 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -158,7 +158,7 @@ private: /// convertible to @a _expectedType. bool expectType(Expression const& _expression, Type const& _expectedType); /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. - void requireLValue(Expression const& _expression); + void requireLValue(Expression const& _expression, bool _ordinaryAssignment); ContractDefinition const* m_scope = nullptr; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6b03c7b58..b711758d6 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -319,6 +319,37 @@ FunctionDefinitionAnnotation& FunctionDefinition::annotation() const return initAnnotation(); } +FunctionDefinition const& FunctionDefinition::resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart +) const +{ + solAssert(!isConstructor(), ""); + // If we are not doing super-lookup and the function is not virtual, we can stop here. + if (_searchStart == nullptr && !virtualSemantics()) + return *this; + + solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + + FunctionType const* functionType = TypeProvider::function(*this)->asCallableFunction(false); + + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + { + if (_searchStart != nullptr && c != _searchStart) + continue; + _searchStart = nullptr; + for (FunctionDefinition const* function: c->definedFunctions()) + if ( + function->name() == name() && + !function->isConstructor() && + FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(*functionType) + ) + return *function; + } + solAssert(false, "Virtual function " + name() + " not found."); + return *this; // not reached +} + TypePointer ModifierDefinition::type() const { return TypeProvider::modifier(*this); @@ -329,6 +360,33 @@ ModifierDefinitionAnnotation& ModifierDefinition::annotation() const return initAnnotation(); } +ModifierDefinition const& ModifierDefinition::resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart +) const +{ + solAssert(_searchStart == nullptr, "Used super in connection with modifiers."); + + // If we are not doing super-lookup and the modifier is not virtual, we can stop here. + if (_searchStart == nullptr && !virtualSemantics()) + return *this; + + solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + { + if (_searchStart != nullptr && c != _searchStart) + continue; + _searchStart = nullptr; + for (ModifierDefinition const* modifier: c->functionModifiers()) + if (modifier->name() == name()) + return *modifier; + } + solAssert(false, "Virtual modifier " + name() + " not found."); + return *this; // not reached +} + + TypePointer EventDefinition::type() const { return TypeProvider::function(*this); @@ -508,8 +566,6 @@ set VariableDeclaration::allowedDataLocations() c if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter()) return set{ Location::Unspecified }; - else if (isStateVariable() && isConstant()) - return set{ Location::Memory }; else if (isExternalCallableParameter()) { set locations{ Location::CallData }; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index adb957cfe..4979d5d4e 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -62,6 +62,24 @@ class ASTConstVisitor; class ASTNode: private boost::noncopyable { public: + struct CompareByID + { + using is_transparent = void; + + bool operator()(ASTNode const* _lhs, ASTNode const* _rhs) const + { + return _lhs->id() < _rhs->id(); + } + bool operator()(ASTNode const* _lhs, int64_t _rhs) const + { + return _lhs->id() < _rhs; + } + bool operator()(int64_t _lhs, ASTNode const* _rhs) const + { + return _lhs < _rhs->id(); + } + }; + using SourceLocation = langutil::SourceLocation; explicit ASTNode(int64_t _id, SourceLocation const& _location); @@ -689,6 +707,18 @@ public: CallableDeclarationAnnotation& annotation() const override = 0; + /// Performs virtual or super function/modifier lookup: + /// If @a _searchStart is nullptr, performs virtual function lookup, i.e. + /// searches the inheritance hierarchy of @a _mostDerivedContract towards the base + /// and returns the first function/modifier definition that + /// is overwritten by this callable. + /// If @a _searchStart is non-null, starts searching only from that contract, but + /// still in the hierarchy of @a _mostDerivedContract. + virtual CallableDeclaration const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const = 0; + protected: ASTPointer m_parameters; ASTPointer m_overrides; @@ -799,6 +829,12 @@ public: CallableDeclaration::virtualSemantics() || (annotation().contract && annotation().contract->isInterface()); } + + FunctionDefinition const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const override; + private: StateMutability m_stateMutability; Token const m_kind; @@ -879,6 +915,7 @@ public: bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_constantness == Constantness::Constant; } + bool immutable() const { return m_constantness == Constantness::Immutable; } ASTPointer const& overrides() const { return m_overrides; } Location referenceLocation() const { return m_location; } /// @returns a set of allowed storage locations for the variable. @@ -944,6 +981,12 @@ public: ModifierDefinitionAnnotation& annotation() const override; + ModifierDefinition const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const override; + + private: ASTPointer m_body; }; @@ -1009,6 +1052,14 @@ public: EventDefinitionAnnotation& annotation() const override; + CallableDeclaration const& resolveVirtual( + ContractDefinition const&, + ContractDefinition const* + ) const override + { + return *this; + } + private: bool m_anonymous = false; }; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 86636684c..021a742f5 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -47,6 +47,14 @@ using TypePointer = Type const*; struct ASTAnnotation { + ASTAnnotation() = default; + + ASTAnnotation(ASTAnnotation const&) = delete; + ASTAnnotation(ASTAnnotation&&) = delete; + + ASTAnnotation& operator=(ASTAnnotation const&) = delete; + ASTAnnotation& operator=(ASTAnnotation&&) = delete; + virtual ~ASTAnnotation() = default; }; @@ -58,7 +66,16 @@ struct DocTag struct StructurallyDocumentedAnnotation { + StructurallyDocumentedAnnotation() = default; + + StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation const&) = delete; + StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation&&) = delete; + + StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation const&) = delete; + StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation&&) = delete; + virtual ~StructurallyDocumentedAnnotation() = default; + /// Mapping docstring tag name -> content. std::multimap docTags; }; @@ -75,6 +92,16 @@ struct SourceUnitAnnotation: ASTAnnotation struct ScopableAnnotation { + ScopableAnnotation() = default; + + ScopableAnnotation(ScopableAnnotation const&) = delete; + ScopableAnnotation(ScopableAnnotation&&) = delete; + + ScopableAnnotation& operator=(ScopableAnnotation const&) = delete; + ScopableAnnotation& operator=(ScopableAnnotation&&) = delete; + + virtual ~ScopableAnnotation() = default; + /// The scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. ASTNode const* scope = nullptr; @@ -208,6 +235,9 @@ struct ExpressionAnnotation: ASTAnnotation bool isLValue = false; /// Whether the expression is used in a context where the LValue is actually required. bool lValueRequested = false; + /// Whether the expression is an lvalue that is only assigned. + /// Would be false for --, ++, delete, +=, -=, .... + bool lValueOfOrdinaryAssignment = false; /// Types and - if given - names of arguments if the expr. is a function /// that is called, used for overload resoultion diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp index d903a1b4b..483b75301 100644 --- a/libsolidity/ast/ASTUtils.cpp +++ b/libsolidity/ast/ASTUtils.cpp @@ -21,7 +21,7 @@ namespace solidity::frontend { -VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl) +VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl) { solAssert(_varDecl.isConstant(), "Constant variable expected"); @@ -30,7 +30,8 @@ VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _v while ((identifier = dynamic_cast(rootDecl->value().get()))) { auto referencedVarDecl = dynamic_cast(identifier->annotation().referencedDeclaration); - solAssert(referencedVarDecl && referencedVarDecl->isConstant(), "Identifier is not referencing a variable declaration"); + if (!referencedVarDecl || !referencedVarDecl->isConstant()) + return nullptr; rootDecl = referencedVarDecl; } return rootDecl; diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h index 7624080a9..af77b60f1 100644 --- a/libsolidity/ast/ASTUtils.h +++ b/libsolidity/ast/ASTUtils.h @@ -22,8 +22,9 @@ namespace solidity::frontend class VariableDeclaration; -/// Find the topmost referenced variable declaration when the given variable +/// Find the topmost referenced constant variable declaration when the given variable /// declaration value is an identifier. Works only for constant variable declarations. -VariableDeclaration const* rootVariableDeclaration(VariableDeclaration const& _varDecl); +/// Returns nullptr if an identifier in the chain is not referencing a constant variable declaration. +VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl); } diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 90dad003a..db67dc392 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -41,7 +41,16 @@ namespace solidity::frontend class ASTVisitor { public: + ASTVisitor() = default; + + ASTVisitor(ASTVisitor const&) = delete; + ASTVisitor(ASTVisitor&&) = delete; + + ASTVisitor& operator=(ASTVisitor const&) = delete; + ASTVisitor& operator=(ASTVisitor&&) = delete; + virtual ~ASTVisitor() = default; + virtual bool visit(SourceUnit& _node) { return visitNode(_node); } virtual bool visit(PragmaDirective& _node) { return visitNode(_node); } virtual bool visit(ImportDirective& _node) { return visitNode(_node); } @@ -158,7 +167,16 @@ protected: class ASTConstVisitor { public: + ASTConstVisitor() = default; + + ASTConstVisitor(ASTConstVisitor const&) = delete; + ASTConstVisitor(ASTConstVisitor&&) = delete; + + ASTConstVisitor& operator=(ASTConstVisitor const&) = delete; + ASTConstVisitor& operator=(ASTConstVisitor&&) = delete; + virtual ~ASTConstVisitor() = default; + virtual bool visit(SourceUnit const& _node) { return visitNode(_node); } virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); } virtual bool visit(ImportDirective const& _node) { return visitNode(_node); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 3f54fccc6..3fc649caf 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1487,11 +1487,19 @@ TypeResult ReferenceType::unaryOperatorResult(Token _operator) const case DataLocation::Memory: return TypeProvider::emptyTuple(); case DataLocation::Storage: - return m_isPointer ? nullptr : TypeProvider::emptyTuple(); + return isPointer() ? nullptr : TypeProvider::emptyTuple(); } return nullptr; } +bool ReferenceType::isPointer() const +{ + if (m_location == DataLocation::Storage) + return m_isPointer; + else + return true; +} + TypePointer ReferenceType::copyForLocationIfReference(Type const* _type) const { return TypeProvider::withLocationIfReference(m_location, _type); @@ -1502,7 +1510,7 @@ string ReferenceType::stringForReferencePart() const switch (m_location) { case DataLocation::Storage: - return string("storage ") + (m_isPointer ? "pointer" : "ref"); + return string("storage ") + (isPointer() ? "pointer" : "ref"); case DataLocation::CallData: return "calldata"; case DataLocation::Memory: @@ -1868,7 +1876,8 @@ u256 ArrayType::memoryDataSize() const std::unique_ptr ArrayType::copyForLocation(DataLocation _location, bool _isPointer) const { auto copy = make_unique(_location); - copy->m_isPointer = _isPointer; + if (_location == DataLocation::Storage) + copy->m_isPointer = _isPointer; copy->m_arrayKind = m_arrayKind; copy->m_baseType = copy->copyForLocationIfReference(m_baseType); copy->m_hasDynamicLength = m_hasDynamicLength; @@ -1988,7 +1997,7 @@ vector> ContractType::stateVar vector variables; for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) for (VariableDeclaration const* variable: contract->stateVariables()) - if (!variable->isConstant()) + if (!(variable->isConstant() || variable->immutable())) variables.push_back(variable); TypePointers types; for (auto variable: variables) @@ -2003,6 +2012,16 @@ vector> ContractType::stateVar return variablesAndOffsets; } +vector ContractType::immutableVariables() const +{ + vector variables; + for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) + for (VariableDeclaration const* variable: contract->stateVariables()) + if (variable->immutable()) + variables.push_back(variable); + return variables; +} + vector> ContractType::makeStackItems() const { if (m_super) @@ -2247,7 +2266,8 @@ TypeResult StructType::interfaceType(bool _inLibrary) const std::unique_ptr StructType::copyForLocation(DataLocation _location, bool _isPointer) const { auto copy = make_unique(m_struct, _location); - copy->m_isPointer = _isPointer; + if (_location == DataLocation::Storage) + copy->m_isPointer = _isPointer; return copy; } @@ -2318,7 +2338,7 @@ TypePointers StructType::memoryMemberTypes() const TypePointers types; for (ASTPointer const& variable: m_struct.members()) if (variable->annotation().type->canLiveOutsideStorage()) - types.push_back(variable->annotation().type); + types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type)); return types; } @@ -2954,7 +2974,7 @@ vector> FunctionType::makeStackItems() const if (m_valueSet) slots.emplace_back("value", TypeProvider::uint256()); if (m_saltSet) - slots.emplace_back("salt", TypeProvider::uint256()); + slots.emplace_back("salt", TypeProvider::fixedBytes(32)); if (bound()) for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems()) slots.emplace_back("self_" + boundName, boundType); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 5aada86c3..d0e774cb7 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -701,7 +701,9 @@ public: /// pointer type, state variables are bound references. Assignments to pointers or deleting /// them will not modify storage (that will only change the pointer). Assignment from /// non-storage objects to a variable of storage pointer type is not possible. - bool isPointer() const { return m_isPointer; } + /// For anything other than storage, this always returns true because assignments + /// never change the contents of the original value. + bool isPointer() const; bool operator==(ReferenceType const& _other) const { @@ -893,6 +895,8 @@ public: /// @returns a list of all state variables (including inherited) of the contract and their /// offsets in storage. std::vector> stateVariables() const; + /// @returns a list of all immutable variables (including inherited) of the contract. + std::vector immutableVariables() const; protected: std::vector> makeStackItems() const override; private: diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index cb195c49f..06080afbd 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -71,6 +71,49 @@ void CompilerContext::addStateVariable( m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset); } +void CompilerContext::addImmutable(VariableDeclaration const& _variable) +{ + solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable."); + solUnimplementedAssert(_variable.annotation().type->isValueType(), "Only immutable variables of value type are supported."); + solAssert(m_runtimeContext, "Attempted to register an immutable variable for runtime code generation."); + m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory; + solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap."); + *m_reservedMemory += _variable.annotation().type->memoryHeadSize(); +} + +size_t CompilerContext::immutableMemoryOffset(VariableDeclaration const& _variable) const +{ + solAssert(m_immutableVariables.count(&_variable), "Memory offset of unknown immutable queried."); + solAssert(m_runtimeContext, "Attempted to fetch the memory offset of an immutable variable during runtime code generation."); + return m_immutableVariables.at(&_variable); +} + +vector CompilerContext::immutableVariableSlotNames(VariableDeclaration const& _variable) +{ + string baseName = to_string(_variable.id()); + solAssert(_variable.annotation().type->sizeOnStack() > 0, ""); + if (_variable.annotation().type->sizeOnStack() == 1) + return {baseName}; + vector names; + auto collectSlotNames = [&](string const& _baseName, TypePointer type, auto const& _recurse) -> void { + for (auto const& [slot, type]: type->stackItems()) + if (type) + _recurse(_baseName + " " + slot, type, _recurse); + else + names.emplace_back(_baseName); + }; + collectSlotNames(baseName, _variable.annotation().type, collectSlotNames); + return names; +} + +size_t CompilerContext::reservedMemory() +{ + solAssert(m_reservedMemory.has_value(), "Reserved memory was used before "); + size_t reservedMemory = *m_reservedMemory; + m_reservedMemory = std::nullopt; + return reservedMemory; +} + void CompilerContext::startFunction(Declaration const& _function) { m_functionCompilationQueue.startFunction(_function); @@ -223,57 +266,43 @@ evmasm::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration con return m_functionCompilationQueue.entryLabelIfExists(_declaration); } -FunctionDefinition const& CompilerContext::resolveVirtualFunction(FunctionDefinition const& _function) -{ - // Libraries do not allow inheritance and their functions can be inlined, so we should not - // search the inheritance hierarchy (which will be the wrong one in case the function - // is inlined). - if (auto scope = dynamic_cast(_function.scope())) - if (scope->isLibrary()) - return _function; - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - return resolveVirtualFunction(_function, m_inheritanceHierarchy.begin()); -} - FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base) { - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - return resolveVirtualFunction(_function, superContract(_base)); + solAssert(m_mostDerivedContract, "No most derived contract set."); + ContractDefinition const* super = superContract(_base); + solAssert(super, "Super contract not available."); + return _function.resolveVirtual(mostDerivedContract(), super); } FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const { - vector::const_iterator it = superContract(_contract); - for (; it != m_inheritanceHierarchy.end(); ++it) - if ((*it)->constructor()) - return (*it)->constructor(); + ContractDefinition const* next = superContract(_contract); + if (next == nullptr) + return nullptr; + for (ContractDefinition const* c: m_mostDerivedContract->annotation().linearizedBaseContracts) + if (next != nullptr && next != c) + continue; + else + { + next = nullptr; + if (c->constructor()) + return c->constructor(); + } return nullptr; } +ContractDefinition const& CompilerContext::mostDerivedContract() const +{ + solAssert(m_mostDerivedContract, "Most derived contract not set."); + return *m_mostDerivedContract; +} + Declaration const* CompilerContext::nextFunctionToCompile() const { return m_functionCompilationQueue.nextFunctionToCompile(); } -ModifierDefinition const& CompilerContext::resolveVirtualFunctionModifier( - ModifierDefinition const& _modifier -) const -{ - // Libraries do not allow inheritance and their functions can be inlined, so we should not - // search the inheritance hierarchy (which will be the wrong one in case the function - // is inlined). - if (auto scope = dynamic_cast(_modifier.scope())) - if (scope->isLibrary()) - return _modifier; - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - for (ContractDefinition const* contract: m_inheritanceHierarchy) - for (ModifierDefinition const* modifier: contract->functionModifiers()) - if (modifier->name() == _modifier.name()) - return *modifier; - solAssert(false, "Function modifier " + _modifier.name() + " not found in inheritance hierarchy."); -} - unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const { auto res = m_localVariables.find(&_declaration); @@ -500,32 +529,26 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _ #endif } -FunctionDefinition const& CompilerContext::resolveVirtualFunction( - FunctionDefinition const& _function, - vector::const_iterator _searchStart -) +LinkerObject const& CompilerContext::assembledObject() const { - string name = _function.name(); - FunctionType functionType(_function); - auto it = _searchStart; - for (; it != m_inheritanceHierarchy.end(); ++it) - for (FunctionDefinition const* function: (*it)->definedFunctions()) - if ( - function->name() == name && - !function->isConstructor() && - FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) - ) - return *function; - solAssert(false, "Super function " + name + " not found."); - return _function; // not reached + LinkerObject const& object = m_asm->assemble(); + solAssert(object.immutableReferences.empty(), "Leftover immutables."); + return object; } -vector::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const +ContractDefinition const* CompilerContext::superContract(ContractDefinition const& _contract) const { - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_contract); - solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy."); - return ++it; + auto const& hierarchy = mostDerivedContract().annotation().linearizedBaseContracts; + auto it = find(hierarchy.begin(), hierarchy.end(), &_contract); + solAssert(it != hierarchy.end(), "Base not found in inheritance hierarchy."); + ++it; + if (it == hierarchy.end()) + return nullptr; + else + { + solAssert(*it != &_contract, ""); + return *it; + } } string CompilerContext::revertReasonIfDebug(string const& _message) diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 8c5775239..a8e494435 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -64,6 +64,7 @@ public: m_asm(std::make_shared()), m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), + m_reservedMemory{0}, m_runtimeContext(_runtimeContext), m_abiFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector), m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector) @@ -80,6 +81,16 @@ public: bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); + void addImmutable(VariableDeclaration const& _declaration); + + /// @returns the reserved memory for storing the value of the immutable @a _variable during contract creation. + size_t immutableMemoryOffset(VariableDeclaration const& _variable) const; + /// @returns a list of slot names referring to the stack slots of an immutable variable. + static std::vector immutableVariableSlotNames(VariableDeclaration const& _variable); + + /// @returns the reserved memory and resets it to mark it as used. + size_t reservedMemory(); + void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void removeVariable(Declaration const& _declaration); /// Removes all local variables currently allocated above _stackHeight. @@ -103,15 +114,14 @@ public: /// @returns the entry label of the given function. Might return an AssemblyItem of type /// UndefinedItem if it does not exist yet. evmasm::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const; - /// @returns the entry label of the given function and takes overrides into account. - FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function); /// @returns the function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base); /// @returns the next constructor in the inheritance hierarchy. FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const; - /// Sets the current inheritance hierarchy from derived to base. - void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } + /// Sets the contract currently being compiled - the most derived one. + void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; } + ContractDefinition const& mostDerivedContract() const; /// @returns the next function in the queue of functions that are still to be compiled /// (i.e. that were referenced during compilation but where we did not yet generate code for). @@ -160,7 +170,6 @@ public: /// empty return value. std::pair> requestedYulFunctions(); - ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; /// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns @@ -217,6 +226,10 @@ public: evmasm::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); } /// Appends the address (virtual, will be filled in by linker) of a library. void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); } + /// Appends an immutable variable. The value will be filled in by the constructor. + void appendImmutable(std::string const& _identifier) { m_asm->appendImmutable(_identifier); } + /// Appends an assignment to an immutable variable. Only valid in creation code. + void appendImmutableAssignment(std::string const& _identifier) { m_asm->appendImmutableAssignment(_identifier); } /// Appends a zero-address that can be replaced by something else at deploy time (if the /// position in bytecode is known). void appendDeployTimeAddress() { m_asm->append(evmasm::PushDeployTimeAddress); } @@ -282,7 +295,7 @@ public: return m_asm->assemblyJSON(_indicies); } - evmasm::LinkerObject const& assembledObject() const { return m_asm->assemble(); } + evmasm::LinkerObject const& assembledObject() const; evmasm::LinkerObject const& assembledRuntimeObject(size_t _subIndex) const { return m_asm->sub(_subIndex).assemble(); } /** @@ -300,14 +313,8 @@ public: RevertStrings revertStrings() const { return m_revertStrings; } private: - /// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns - /// the first function definition that is overwritten by _function. - FunctionDefinition const& resolveVirtualFunction( - FunctionDefinition const& _function, - std::vector::const_iterator _searchStart - ); - /// @returns an iterator to the contract directly above the given contract. - std::vector::const_iterator superContract(ContractDefinition const& _contract) const; + /// @returns a pointer to the contract directly above the given contract. + ContractDefinition const* superContract(ContractDefinition const& _contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); @@ -355,13 +362,19 @@ private: std::map> m_otherCompilers; /// Storage offsets of state variables std::map> m_stateVariables; + /// Memory offsets reserved for the values of immutable variables during contract creation. + std::map m_immutableVariables; + /// Total amount of reserved memory. Reserved memory is used to store immutable variables during contract creation. + /// This has to be finalized before initialiseFreeMemoryPointer() is called. That function + /// will reset the optional to verify that. + std::optional m_reservedMemory = {0}; /// Offsets of local variables on the stack (relative to stack base). /// This needs to be a stack because if a modifier contains a local variable and this /// modifier is applied twice, the position of the variable needs to be restored /// after the nested modifier is left. std::map> m_localVariables; - /// List of current inheritance hierarchy from derived to base. - std::vector m_inheritanceHierarchy; + /// The contract currently being compiled. Virtual function lookup starts from this contarct. + ContractDefinition const* m_mostDerivedContract = nullptr; /// Stack of current visited AST nodes, used for location attachment std::stack m_visitedNodes; /// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index c559c0b7c..d4f35ad79 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -51,7 +51,9 @@ static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPoi void CompilerUtils::initialiseFreeMemoryPointer() { - m_context << u256(generalPurposeMemoryStart); + size_t reservedMemory = m_context.reservedMemory(); + solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, ""); + m_context << (u256(generalPurposeMemoryStart) + reservedMemory); storeFreeMemoryPointer(); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 8b9061af3..822ce3b66 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -129,7 +129,9 @@ void ContractCompiler::initializeContext( { m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); m_context.setOtherCompilers(_otherCompilers); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); + if (m_runtimeCompiler) + registerImmutableVariables(_contract); CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); m_context.resetVisitedNodes(&_contract); @@ -183,10 +185,26 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont m_context << deployRoutine; solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); + + ContractType contractType(_contract); + auto const& immutables = contractType.immutableVariables(); + // Push all immutable values on the stack. + for (auto const& immutable: immutables) + CompilerUtils(m_context).loadFromMemory(m_context.immutableMemoryOffset(*immutable), *immutable->annotation().type); m_context.pushSubroutineSize(m_context.runtimeSub()); - m_context << Instruction::DUP1; + if (immutables.empty()) + m_context << Instruction::DUP1; m_context.pushSubroutineOffset(m_context.runtimeSub()); m_context << u256(0) << Instruction::CODECOPY; + // Assign immutable values from stack in reversed order. + for (auto const& immutable: immutables | boost::adaptors::reversed) + { + auto slotNames = m_context.immutableVariableSlotNames(*immutable); + for (auto&& slotName: slotNames | boost::adaptors::reversed) + m_context.appendImmutableAssignment(slotName); + } + if (!immutables.empty()) + m_context.pushSubroutineSize(m_context.runtimeSub()); m_context << u256(0) << Instruction::RETURN; return m_context.runtimeSub(); @@ -521,6 +539,13 @@ void ContractCompiler::registerStateVariables(ContractDefinition const& _contrac m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } +void ContractCompiler::registerImmutableVariables(ContractDefinition const& _contract) +{ + solAssert(m_runtimeCompiler, "Attempted to register immutables for runtime code generation."); + for (auto const& var: ContractType(_contract).immutableVariables()) + m_context.addImmutable(*var); +} + void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract) { solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); @@ -662,7 +687,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (FunctionDefinition const* functionDef = dynamic_cast(decl)) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); - functionDef = &m_context.resolveVirtualFunction(*functionDef); + functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag(); solAssert(functionEntryLabel.data() <= std::numeric_limits::max(), ""); _assembly.appendLabelReference(size_t(functionEntryLabel.data())); @@ -680,9 +705,15 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (auto variable = dynamic_cast(decl)) { + solAssert(!variable->immutable(), ""); if (variable->isConstant()) { - variable = rootVariableDeclaration(*variable); + variable = rootConstVariableDeclaration(*variable); + // If rootConstVariableDeclaration fails and returns nullptr, + // it should have failed in TypeChecker already, causing a compilation error. + // In such case we should not get here. + solAssert(variable, ""); + u256 value; if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) { @@ -1302,10 +1333,9 @@ void ContractCompiler::appendModifierOrFunctionCode() appendModifierOrFunctionCode(); else { - ModifierDefinition const& nonVirtualModifier = dynamic_cast( + ModifierDefinition const& modifier = dynamic_cast( *modifierInvocation->name()->annotation().referencedDeclaration - ); - ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier); + ).resolveVirtual(m_context.mostDerivedContract()); CompilerContext::LocationSetter locationSetter(m_context, modifier); std::vector> const& modifierArguments = modifierInvocation->arguments() ? *modifierInvocation->arguments() : std::vector>(); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 0a2ecf7e8..0916da281 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -99,6 +99,7 @@ private: void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); void registerStateVariables(ContractDefinition const& _contract); + void registerImmutableVariables(ContractDefinition const& _contract); void initializeStateVariables(ContractDefinition const& _contract); bool visit(VariableDeclaration const& _variableDeclaration) override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 0fc24d919..a02497561 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -73,7 +73,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c utils().convertType(*type, *_varDecl.annotation().type); type = _varDecl.annotation().type; } - StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); + if (_varDecl.immutable()) + ImmutableItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); + else + StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); } void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration const& _varDecl) @@ -93,11 +96,17 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& FunctionType accessorType(_varDecl); TypePointers paramTypes = accessorType.parameterTypes(); + if (_varDecl.immutable()) + solAssert(paramTypes.empty(), ""); + m_context.adjustStackOffset(1 + CompilerUtils::sizeOnStack(paramTypes)); - // retrieve the position of the variable - auto const& location = m_context.storageLocationOfVariable(_varDecl); - m_context << location.first << u256(location.second); + if (!_varDecl.immutable()) + { + // retrieve the position of the variable + auto const& location = m_context.storageLocationOfVariable(_varDecl); + m_context << location.first << u256(location.second); + } TypePointer returnType = _varDecl.annotation().type; @@ -179,6 +188,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& solAssert(returnTypes.size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType)) { + solAssert(!_varDecl.immutable(), ""); // remove offset m_context << Instruction::POP; auto const& names = accessorType.returnParameterNames(); @@ -205,7 +215,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { // simple value or array solAssert(returnTypes.size() == 1, ""); - StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); + if (_varDecl.immutable()) + ImmutableItem(m_context, _varDecl).retrieveValue(SourceLocation()); + else + StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); utils().convertType(*returnType, *returnTypes.front()); retSizeOnStack = returnTypes.front()->sizeOnStack(); } @@ -572,7 +585,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Do not directly visit the identifier, because this way, we can avoid // the runtime entry label to be created at the creation time context. CompilerContext::LocationSetter locationSetter2(m_context, *identifier); - utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); + utils().pushCombinedFunctionEntryLabel( + functionDef->resolveVirtual(m_context.mostDerivedContract()), + false + ); shortcutTaken = true; } } @@ -992,6 +1008,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Fetch requested length. acceptAndConvert(*arguments[0], *TypeProvider::uint256()); + // Make sure we can allocate memory without overflow + m_context << u256(0xffffffffffffffff); + m_context << Instruction::DUP2; + m_context << Instruction::GT; + m_context.appendConditionalRevert(); + // Stack: requested_length utils().fetchFreeMemoryPointer(); @@ -1800,6 +1822,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) { CompilerContext::LocationSetter locationSetter(m_context, _indexAccess); _indexAccess.baseExpression().accept(*this); + // stack: offset length Type const& baseType = *_indexAccess.baseExpression().annotation().type; @@ -1815,27 +1838,21 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256()); else m_context << u256(0); + // stack: offset length sliceStart + + m_context << Instruction::SWAP1; + // stack: offset sliceStart length + if (_indexAccess.endExpression()) acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256()); else - m_context << Instruction::DUP2; + m_context << Instruction::DUP1; + // stack: offset sliceStart length sliceEnd - m_context.appendInlineAssembly( - Whiskers(R"({ - if gt(sliceStart, sliceEnd) { } - if gt(sliceEnd, length) { } + m_context << Instruction::SWAP3; + // stack: sliceEnd sliceStart length offset - offset := add(offset, mul(sliceStart, )) - length := sub(sliceEnd, sliceStart) - })") - ("stride", toString(arrayType->calldataStride())) - ("revertStringStartEnd", m_context.revertReasonIfDebug("Slice starts after end")) - ("revertStringEndLength", m_context.revertReasonIfDebug("Slice is greater than length")) - .render(), - {"offset", "length", "sliceStart", "sliceEnd"} - ); - - m_context << Instruction::POP << Instruction::POP; + m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2); return false; } @@ -1866,7 +1883,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) // we want to avoid having a reference to the runtime function entry point in the // constructor context, since this would force the compiler to include unreferenced // internal functions in the runtime contex. - utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef)); + utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract())); else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); else if (auto contract = dynamic_cast(declaration)) @@ -2438,10 +2455,12 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression) { - if (!_variable.isConstant()) - setLValueFromDeclaration(_variable, _expression); - else + if (_variable.isConstant()) acceptAndConvert(*_variable.value(), *_variable.annotation().type); + else if (_variable.immutable()) + setLValue(_expression, _variable); + else + setLValueFromDeclaration(_variable, _expression); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 5fd17bf5c..3a04d6f92 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -144,9 +144,46 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const m_context << Instruction::POP; } + +ImmutableItem::ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable): + LValue(_compilerContext, _variable.annotation().type), m_variable(_variable) +{ + solAssert(_variable.immutable(), ""); +} + +void ImmutableItem::retrieveValue(SourceLocation const&, bool) const +{ + solUnimplementedAssert(m_dataType->isValueType(), ""); + solAssert(!m_context.runtimeContext(), "Tried to read immutable at construction time."); + for (auto&& slotName: m_context.immutableVariableSlotNames(m_variable)) + m_context.appendImmutable(slotName); +} + +void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const +{ + CompilerUtils utils(m_context); + solUnimplementedAssert(m_dataType->isValueType(), ""); + solAssert(_sourceType.isValueType(), ""); + + utils.convertType(_sourceType, *m_dataType, true); + m_context << m_context.immutableMemoryOffset(m_variable); + if (_move) + utils.moveIntoStack(m_dataType->sizeOnStack()); + else + utils.copyToStackTop(m_dataType->sizeOnStack() + 1, m_dataType->sizeOnStack()); + utils.storeInMemoryDynamic(*m_dataType, false); + m_context << Instruction::POP; +} + +void ImmutableItem::setToZero(SourceLocation const&, bool) const +{ + solAssert(false, "Attempted to set immutable variable to zero."); +} + StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): StorageItem(_compilerContext, *_declaration.annotation().type) { + solAssert(!_declaration.immutable(), ""); auto const& location = m_context.storageLocationOfVariable(_declaration); m_context << location.first << u256(location.second); } diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index 1cd4def01..cd72f37e0 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -82,12 +83,12 @@ public: unsigned sizeOnStack() const override { return 0; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -108,12 +109,12 @@ public: MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true); unsigned sizeOnStack() const override { return 1; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -122,6 +123,30 @@ private: bool m_padded = false; }; +/** + * Reference to an immutable variable. During contract creation this refers to a location in memory. At the + * end of contract creation the values from these memory locations are copied into all occurrences of the immutable + * variable in the runtime code. + */ +class ImmutableItem: public LValue +{ +public: + ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable); + unsigned sizeOnStack() const override { return 0; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; + void storeValue( + Type const& _sourceType, + langutil::SourceLocation const& _location = {}, + bool _move = false + ) const override; + void setToZero( + langutil::SourceLocation const& _location = {}, + bool _removeReference = true + ) const override; +private: + VariableDeclaration const& m_variable; +}; + /** * Reference to some item in storage. On the stack this is , * where 0 <= offset_inside_value < 32 and an offset of i means that the value is multiplied @@ -136,12 +161,12 @@ public: StorageItem(CompilerContext& _compilerContext, Type const& _type); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -158,12 +183,12 @@ public: StorageByteArrayElement(CompilerContext& _compilerContext); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -180,12 +205,12 @@ public: TupleObject(CompilerContext& _compilerContext, std::vector>&& _lvalues); unsigned sizeOnStack() const override; void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 046769e43..28a4f69fb 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -658,6 +658,8 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!"); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); + solAssert(_type.baseType()->isValueType(), ""); + string functionName = "array_push_zero_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( @@ -794,6 +796,7 @@ string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) }); } + string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); @@ -933,6 +936,28 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type }); } +string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type) +{ + solAssert(_type.dataStoredIn(DataLocation::CallData), ""); + solAssert(_type.isDynamicallySized(), ""); + string functionName = "calldata_array_index_range_access_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (offset, length, startIndex, endIndex) -> offsetOut, lengthOut { + if gt(startIndex, endIndex) { } + if gt(endIndex, length) { } + offsetOut := add(offset, mul(startIndex, )) + lengthOut := sub(endIndex, startIndex) + } + )") + ("functionName", functionName) + ("stride", to_string(_type.calldataStride())) + ("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end")) + ("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length")) + .render(); + }); +} + string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) { solAssert(_type.isDynamicallyEncoded(), ""); @@ -1156,7 +1181,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) return Whiskers(R"( function (memPtr, value) { mstore(memPtr, value) - } + } )") ("functionName", functionName) .render(); @@ -1180,7 +1205,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) return Whiskers(R"( function (memPtr, value) { mstore(memPtr, (value)) - } + } )") ("functionName", functionName) ("cleanup", cleanupFunction(_type)) @@ -1312,28 +1337,112 @@ string YulUtilFunctions::allocationFunction() }); } -string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type) +string YulUtilFunctions::zeroMemoryArrayFunction(ArrayType const& _type) { - solUnimplementedAssert(!_type.isByteArray(), ""); + if (_type.baseType()->hasSimpleZeroValueInMemory()) + return zeroMemoryFunction(*_type.baseType()); + return zeroComplexMemoryArrayFunction(_type); +} - string functionName = "allocate_memory_array_" + _type.identifier(); +string YulUtilFunctions::zeroMemoryFunction(Type const& _type) +{ + solAssert(_type.hasSimpleZeroValueInMemory(), ""); + + string functionName = "zero_memory_chunk_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (length) -> memPtr { - memPtr := ((length)) - - mstore(memPtr, length) - + function (dataStart, dataSizeInBytes) { + calldatacopy(dataStart, calldatasize(), dataSizeInBytes) } )") ("functionName", functionName) - ("alloc", allocationFunction()) - ("allocSize", arrayAllocationSizeFunction(_type)) - ("dynamic", _type.isDynamicallySized()) .render(); }); } +string YulUtilFunctions::zeroComplexMemoryArrayFunction(ArrayType const& _type) +{ + solAssert(!_type.baseType()->hasSimpleZeroValueInMemory(), ""); + + string functionName = "zero_complex_memory_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + solAssert(_type.memoryStride() == 32, ""); + return Whiskers(R"( + function (dataStart, dataSizeInBytes) { + for {let i := 0} lt(i, dataSizeInBytes) { i := add(i, ) } { + mstore(add(dataStart, i), ()) + } + } + )") + ("functionName", functionName) + ("stride", to_string(_type.memoryStride())) + ("zeroValue", zeroValueFunction(*_type.baseType(), false)) + .render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType const& _type) +{ + solUnimplementedAssert(!_type.isByteArray(), ""); + + string functionName = "allocate_and_zero_memory_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (length) -> memPtr { + let allocSize := (length) + memPtr := (allocSize) + let dataStart := memPtr + let dataSize := allocSize + + dataStart := add(dataStart, 32) + dataSize := sub(dataSize, 32) + mstore(memPtr, length) + + (dataStart, dataSize) + } + )") + ("functionName", functionName) + ("alloc", allocationFunction()) + ("allocSize", arrayAllocationSizeFunction(_type)) + ("zeroArrayFunction", zeroMemoryArrayFunction(_type)) + ("dynamic", _type.isDynamicallySized()) + .render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type) +{ + string functionName = "allocate_and_initialize_memory_struct_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function () -> memPtr { + let allocSize := () + memPtr := (allocSize) + let offset := memPtr + <#member> + mstore(offset, ()) + offset := add(offset, 32) + + } + )"); + templ("functionName", functionName); + templ("alloc", allocationFunction()); + + TypePointers const& members = _type.memoryMemberTypes(); + templ("allocSize", _type.memoryDataSize().str()); + + vector> memberParams(members.size()); + for (size_t i = 0; i < members.size(); ++i) + { + solAssert(members[i]->memoryHeadSize() == 32, ""); + solAssert(members[i]->dataStoredIn(DataLocation::Memory), ""); + memberParams[i]["zeroValue"] = zeroValueFunction(*members[i], false); + } + templ("member", memberParams); + return templ.render(); + }); +} + string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) { if (_from.category() == Type::Category::Function) @@ -1365,6 +1474,37 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) }); } + if (_from.category() == Type::Category::ArraySlice) + { + solAssert(_from.isDynamicallySized(), ""); + solAssert(_from.dataStoredIn(DataLocation::CallData), ""); + solAssert(_to.category() == Type::Category::Array, ""); + + ArraySliceType const& fromType = dynamic_cast(_from); + ArrayType const& targetType = dynamic_cast(_to); + + solAssert( + *fromType.arrayType().baseType() == *targetType.baseType(), + "Converting arrays of different type is not possible" + ); + + string const functionName = + "convert_" + + _from.identifier() + + "_to_" + + _to.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (offset, length) -> outOffset, outLength { + outOffset := offset + outLength := length + } + )") + ("functionName", functionName) + .render(); + }); + } + if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1) return conversionFunctionSpecial(_from, _to); @@ -1831,23 +1971,58 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) }); } -string YulUtilFunctions::zeroValueFunction(Type const& _type) +string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes) { - solUnimplementedAssert(_type.sizeOnStack() == 1, "Stacksize not yet implemented!"); - solUnimplementedAssert(_type.isValueType(), "Zero value for non-value types not yet implemented"); + solAssert(_type.category() != Type::Category::Mapping, ""); - string const functionName = "zero_value_for_" + _type.identifier(); + string const functionName = "zero_value_for_" + string(_splitFunctionTypes ? "split_" : "") + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - return Whiskers(R"( - function () -> ret { - - } - )") + FunctionType const* fType = dynamic_cast(&_type); + if (fType && fType->kind() == FunctionType::Kind::External && _splitFunctionTypes) + return Whiskers(R"( + function () -> retAddress, retFunction { + retAddress := 0 + retFunction := 0 + } + )") ("functionName", functionName) - ("body", "ret := 0x0") .render(); - }); + + Whiskers templ(R"( + function () -> ret { + ret := + } + )"); + templ("functionName", functionName); + + if (_type.isValueType()) + { + solAssert(( + _type.hasSimpleZeroValueInMemory() || + (fType && (fType->kind() == FunctionType::Kind::Internal || fType->kind() == FunctionType::Kind::External)) + ), ""); + templ("zeroValue", "0"); + } + else + { + solAssert(_type.dataStoredIn(DataLocation::Memory), ""); + if (auto const* arrayType = dynamic_cast(&_type)) + { + if (_type.isDynamicallySized()) + templ("zeroValue", to_string(CompilerUtils::zeroPointer)); + else + templ("zeroValue", allocateAndInitializeMemoryArrayFunction(*arrayType) + "(" + to_string(unsigned(arrayType->length())) + ")"); + + } + else if (auto const* structType = dynamic_cast(&_type)) + templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()"); + else + solUnimplementedAssert(false, ""); + } + + return templ.render(); + }); } string YulUtilFunctions::storageSetToZeroFunction(Type const& _type) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index cf50c785f..b902f9f64 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -37,6 +37,7 @@ class Type; class ArrayType; class MappingType; class IntegerType; +class StructType; /** * Component that can generate various useful Yul functions. @@ -154,6 +155,7 @@ public: /// to store an array in memory given its length (internally encoded, not ABI encoded). /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); + /// @returns the name of a function that converts a storage slot number /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer /// for the data position of an array which is stored in that slot / memory area / calldata area. @@ -175,6 +177,11 @@ public: /// signature: (baseRef, index) -> offset[, length] std::string calldataArrayIndexAccessFunction(ArrayType const& _type); + /// @returns the name of a function that returns offset and length for array slice + /// for the given array offset, length and start and end indices for slice + /// signature: (arrayOffset, arrayLength, sliceStart, sliceEnd) -> offset, length + std::string calldataArrayIndexRangeAccess(ArrayType const& _type); + /// @returns the name of a function that follows a calldata tail while performing /// bounds checks. /// signature: (baseRef, tailPointer) -> offset[, length] @@ -245,10 +252,27 @@ public: /// Return value: pointer std::string allocationFunction(); - /// @returns the name of a function that allocates a memory array. + /// @returns the name of a function that zeroes an array. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that zeroes a chunk of memory. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroMemoryFunction(Type const& _type); + + /// @returns the name of a function that zeroes an array + /// where the base does not have simple zero value in memory. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroComplexMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that allocates and zeroes a memory array. /// For dynamic arrays it adds space for length and stores it. /// signature: (length) -> memPtr - std::string allocateMemoryArrayFunction(ArrayType const& _type); + std::string allocateAndInitializeMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that allocates and zeroes a memory struct. + /// signature: (members) -> memPtr + std::string allocateAndInitializeMemoryStructFunction(StructType const& _type); /// @returns the name of the function that converts a value of type @a _from /// to a value of type @a _to. The resulting vale is guaranteed to be in range @@ -283,8 +307,9 @@ public: std::string negateNumberCheckedFunction(Type const& _type); /// @returns the name of a function that returns the zero value for the - /// provided type - std::string zeroValueFunction(Type const& _type); + /// provided type. + /// @param _splitFunctionTypes if false, returns two zeroes + std::string zeroValueFunction(Type const& _type, bool _splitFunctionTypes = true); /// @returns the name of a function that will set the given storage item to /// zero diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 7184247f4..26f6f0309 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -31,6 +32,12 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; +ContractDefinition const& IRGenerationContext::mostDerivedContract() const +{ + solAssert(m_mostDerivedContract, "Most derived contract requested but not set."); + return *m_mostDerivedContract; +} + IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl) { auto const& [it, didInsert] = m_localVariables.emplace( @@ -70,26 +77,9 @@ string IRGenerationContext::functionName(VariableDeclaration const& _varDecl) return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); } -FunctionDefinition const& IRGenerationContext::virtualFunction(FunctionDefinition const& _function) -{ - // @TODO previously, we had to distinguish creation context and runtime context, - // but since we do not work with jump positions anymore, this should not be a problem, right? - string name = _function.name(); - FunctionType functionType(_function); - for (auto const& contract: m_inheritanceHierarchy) - for (FunctionDefinition const* function: contract->definedFunctions()) - if ( - function->name() == name && - !function->isConstructor() && - FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) - ) - return *function; - solAssert(false, "Super function " + name + " not found."); -} - string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration) { - return functionName(virtualFunction(_functionDeclaration)); + return functionName(_functionDeclaration.resolveVirtual(mostDerivedContract())); } string IRGenerationContext::newYulVariable() @@ -120,12 +110,13 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out) templ("arrow", _out > 0 ? "->" : ""); templ("out", suffixedVariableNameList("out_", 0, _out)); vector> functions; - for (auto const& contract: m_inheritanceHierarchy) + for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts) for (FunctionDefinition const* function: contract->definedFunctions()) if ( + FunctionType const* functionType = TypeProvider::function(*function)->asCallableFunction(false); !function->isConstructor() && - function->parameters().size() == _in && - function->returnParameters().size() == _out + TupleType(functionType->parameterTypes()).sizeOnStack() == _in && + TupleType(functionType->returnParameterTypes()).sizeOnStack() == _out ) { // 0 is reserved for uninitialized function pointers diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 473b62482..b0fc92cdb 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -61,11 +61,12 @@ public: MultiUseYulFunctionCollector& functionCollector() { return m_functions; } - /// Sets the current inheritance hierarchy from derived to base. - void setInheritanceHierarchy(std::vector _hierarchy) + /// Sets the most derived contract (the one currently being compiled)> + void setMostDerivedContract(ContractDefinition const& _mostDerivedContract) { - m_inheritanceHierarchy = std::move(_hierarchy); + m_mostDerivedContract = &_mostDerivedContract; } + ContractDefinition const& mostDerivedContract() const; IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); @@ -81,7 +82,6 @@ public: std::string functionName(FunctionDefinition const& _function); std::string functionName(VariableDeclaration const& _varDecl); - FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration); std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration); std::string newYulVariable(); @@ -103,7 +103,7 @@ private: langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; OptimiserSettings m_optimiserSettings; - std::vector m_inheritanceHierarchy; + ContractDefinition const* m_mostDerivedContract = nullptr; std::map m_localVariables; /// Storage offsets of state variables std::map> m_stateVariables; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 8da576055..5ae4b71d7 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -110,7 +110,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) t("functions", m_context.functionCollector().requestedFunctions()); resetContext(_contract); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); t("RuntimeObject", runtimeObjectName(_contract)); t("dispatch", dispatchRoutine(_contract)); for (auto const* contract: _contract.annotation().linearizedBaseContracts) @@ -133,6 +133,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) return m_context.functionCollector().createFunction(functionName, [&]() { Whiskers t(R"( function () { + } )"); @@ -142,9 +143,14 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); t("params", params); string retParams; + string retInit; for (auto const& varDecl: _function.returnParameters()) + { retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); + retInit += generateInitialAssignment(*varDecl); + } t("returns", retParams.empty() ? "" : " -> " + retParams); + t("initReturnVariables", retInit); t("body", generate(_function.body())); return t.render(); }); @@ -157,6 +163,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) Type const* type = _varDecl.annotation().type; solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.immutable(), ""); solAssert(_varDecl.isStateVariable(), ""); if (auto const* mappingType = dynamic_cast(type)) @@ -225,6 +232,13 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) } } +string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl) +{ + IRGeneratorForStatements generator(m_context, m_utils); + generator.initializeLocalVar(_varDecl); + return generator.code(); +} + string IRGenerator::constructorCode(ContractDefinition const& _contract) { // Initialization of state variables in base-to-derived order. @@ -249,7 +263,7 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract) IRGeneratorForStatements generator{m_context, m_utils}; for (VariableDeclaration const* variable: contract->stateVariables()) - if (!variable->isConstant()) + if (!variable->isConstant() && !variable->immutable()) generator.initializeStateVar(*variable); out << generator.code(); @@ -258,11 +272,28 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract) if (constructor) { - solUnimplementedAssert(constructor->parameters().empty(), ""); + ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); + unsigned paramVars = make_shared(constructor->functionType(false)->parameterTypes())->sizeOnStack(); - // TODO base constructors + Whiskers t(R"X( + let programSize := datasize("") + let argSize := sub(codesize(), programSize) - out << m_context.functionName(*constructor) + "()\n"; + let memoryDataOffset := (argSize) + codecopy(memoryDataOffset, programSize, argSize) + + (memoryDataOffset, add(memoryDataOffset, argSize)) + + () + )X"); + t("object", creationObjectName(_contract)); + t("allocate", m_utils.allocationFunction()); + t("assignToParams", paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := "); + t("params", suffixedVariableNameList("param_", 0, paramVars)); + t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true)); + t("constructorName", m_context.functionName(*constructor)); + + out << t.render(); } return out.str(); @@ -388,7 +419,7 @@ void IRGenerator::resetContext(ContractDefinition const& _contract) ); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); for (auto const& var: ContractType(_contract).stateVariables()) m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index d1ec580f6..e0c1dcd4f 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -61,6 +61,9 @@ private: /// Generates a getter for the given declaration and returns its name std::string generateGetter(VariableDeclaration const& _varDecl); + /// Generates code that assigns the initial value of the respective type. + std::string generateInitialAssignment(VariableDeclaration const& _varDecl); + std::string constructorCode(ContractDefinition const& _contract); std::string deployCode(ContractDefinition const& _contract); std::string callValueCheck(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 7ce1785b6..57c8cb152 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -140,6 +140,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va { solAssert(m_context.isStateVariable(_varDecl), "Must be a state variable."); solAssert(!_varDecl.isConstant(), ""); + solAssert(!_varDecl.immutable(), ""); if (_varDecl.value()) { _varDecl.value()->accept(*this); @@ -153,6 +154,19 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va } } +void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) +{ + solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); + + auto const* type = _varDecl.type(); + if (auto const* refType = dynamic_cast(type)) + if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) + return; + + IRVariable zero = zeroValue(*type); + assign(m_context.localVariable(_varDecl), zero); +} + void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { if (Expression const* expression = _varDeclStatement.initialValue()) @@ -178,7 +192,10 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var else for (auto const& decl: _varDeclStatement.declarations()) if (decl) + { declare(m_context.addLocalVariable(*decl)); + initializeLocalVar(*decl); + } } bool IRGeneratorForStatements::visit(Conditional const& _conditional) @@ -567,7 +584,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } define(_functionCall) << - m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) << + m_context.internalDispatch( + TupleType(functionType->parameterTypes()).sizeOnStack(), + TupleType(functionType->returnParameterTypes()).sizeOnStack() + ) << "(" << IRVariable(_functionCall.expression()).part("functionIdentifier").name() << joinHumanReadablePrefixed(args) << @@ -666,7 +686,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); define(_functionCall) << - m_utils.allocateMemoryArrayFunction(arrayType) << + m_utils.allocateAndInitializeMemoryArrayFunction(arrayType) << "(" << value.commaSeparatedList() << ")\n"; @@ -739,6 +759,25 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } } +void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) +{ + FunctionType const& previousType = dynamic_cast(*_options.expression().annotation().type); + + solUnimplementedAssert(!previousType.bound(), ""); + + // Copy over existing values. + for (auto const& item: previousType.stackItems()) + define(IRVariable(_options).part(get<0>(item)), IRVariable(_options.expression()).part(get<0>(item))); + + for (size_t i = 0; i < _options.names().size(); ++i) + { + string const& name = *_options.names()[i]; + solAssert(name == "salt" || name == "gas" || name == "value", ""); + + define(IRVariable(_options).part(name), *_options.options()[i]); + } +} + void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { ASTString const& member = _memberAccess.memberName(); @@ -981,9 +1020,16 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) } }); } - else if (baseType.category() == Type::Category::Array) + else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice) { - ArrayType const& arrayType = dynamic_cast(baseType); + ArrayType const& arrayType = + baseType.category() == Type::Category::Array ? + dynamic_cast(baseType) : + dynamic_cast(baseType).arrayType(); + + if (baseType.category() == Type::Category::ArraySlice) + solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), ""); + solAssert(_indexAccess.indexExpression(), "Index expression expected."); switch (arrayType.location()) @@ -1066,9 +1112,50 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) solAssert(false, "Index access only allowed for mappings or arrays."); } -void IRGeneratorForStatements::endVisit(IndexRangeAccess const&) +void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) { - solUnimplementedAssert(false, "Index range accesses not yet implemented."); + Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; + solAssert( + baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, + "Index range accesses is available only on arrays and array slices." + ); + + ArrayType const& arrayType = + baseType.category() == Type::Category::Array ? + dynamic_cast(baseType) : + dynamic_cast(baseType).arrayType(); + + switch (arrayType.location()) + { + case DataLocation::CallData: + { + solAssert(baseType.isDynamicallySized(), ""); + IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()}; + if (_indexRangeAccess.startExpression()) + define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()}); + else + define(sliceStart) << u256(0) << "\n"; + + IRVariable sliceEnd{ + m_context.newYulVariable(), + *TypeProvider::uint256() + }; + if (_indexRangeAccess.endExpression()) + define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()}); + else + define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length")); + + IRVariable range{_indexRangeAccess}; + define(range) << + m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" << + IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " << + sliceStart.name() << ", " << + sliceEnd.name() << ")\n"; + break; + } + default: + solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays."); + } } void IRGeneratorForStatements::endVisit(Identifier const& _identifier) @@ -1097,13 +1184,14 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) return; } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) - define(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n"; + define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n"; else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { // TODO for the constant case, we have to be careful: // If the value is visited twice, `defineExpression` is called twice on // the same expression. solUnimplementedAssert(!varDecl->isConstant(), ""); + solUnimplementedAssert(!varDecl->immutable(), ""); if (m_context.isLocalVariable(*varDecl)) setLValue(_identifier, IRLValue{ *varDecl->annotation().type, @@ -1367,6 +1455,12 @@ std::ostream& IRGeneratorForStatements::define(IRVariable const& _var) return m_code; } +void IRGeneratorForStatements::declare(IRVariable const& _var) +{ + if (_var.type().sizeOnStack() > 0) + m_code << "let " << _var.commaSeparatedList() << "\n"; +} + void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare) { string output; @@ -1386,10 +1480,15 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable _rhs.commaSeparatedList() << ")\n"; } -void IRGeneratorForStatements::declare(IRVariable const& _var) + +IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes) { - if (_var.type().sizeOnStack() > 0) - m_code << "let " << _var.commaSeparatedList() << "\n"; + IRVariable irVar{ + "zero_value_for_type_" + _type.identifier() + m_context.newYulVariable(), + _type + }; + define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n"; + return irVar; } void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr) @@ -1519,7 +1618,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable solAssert(dynamic_cast(&_lvalue.type), ""); auto const* valueReferenceType = dynamic_cast(&_value.type()); solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), ""); - m_code << "mstore(" + _memory.address + ", " + _value.name() + ")\n"; + m_code << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n"; } }, [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); }, diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 785b02e09..710961cd7 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -46,6 +46,8 @@ public: /// Generates code to initialize the given state variable. void initializeStateVar(VariableDeclaration const& _varDecl); + /// Generates code to initialize the given local variable. + void initializeLocalVar(VariableDeclaration const& _varDecl); void endVisit(VariableDeclarationStatement const& _variableDeclaration) override; bool visit(Conditional const& _conditional) override; @@ -60,6 +62,7 @@ public: void endVisit(UnaryOperation const& _unaryOperation) override; bool visit(BinaryOperation const& _binOp) override; void endVisit(FunctionCall const& _funCall) override; + void endVisit(FunctionCallOptions const& _funCallOptions) override; void endVisit(MemberAccess const& _memberAccess) override; bool visit(InlineAssembly const& _inlineAsm) override; void endVisit(IndexAccess const& _indexAccess) override; @@ -99,6 +102,11 @@ private: void declareAssign(IRVariable const& _var, IRVariable const& _value, bool _define); + /// @returns an IRVariable with the zero + /// value of @a _type. + /// @param _splitFunctionTypes if false, returns two zeroes + IRVariable zeroValue(Type const& _type, bool _splitFunctionTypes = true); + void appendAndOrOperatorCode(BinaryOperation const& _binOp); void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr); diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index dd1e5136c..141a3eb32 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -432,12 +432,6 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) m_context.newValue(*param); m_context.setUnknownValue(*param); } - - m_errorReporter.warning( - _funCall.location(), - "Assertion checker does not support recursive function calls.", - SecondarySourceLocation().append("Starting from function:", funDef->location()) - ); } else { diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index af50b2722..e10c16d39 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -79,10 +79,9 @@ void CHC::analyze(SourceUnit const& _source) resetSourceAnalysis(); - auto boolSort = make_shared(smt::Kind::Bool); auto genesisSort = make_shared( vector(), - boolSort + smt::SortProvider::boolSort ); m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); addRule(genesis(), "genesis"); @@ -131,12 +130,9 @@ bool CHC::visit(ContractDefinition const& _contract) clearIndices(&_contract); - - // TODO create static instances for Bool/Int sorts in SolverInterface. - auto boolSort = make_shared(smt::Kind::Bool); auto errorFunctionSort = make_shared( vector(), - boolSort + smt::SortProvider::boolSort ); string suffix = _contract.name() + "_" + to_string(_contract.id()); @@ -459,6 +455,8 @@ void CHC::endVisit(FunctionCall const& _funCall) SMTEncoder::endVisit(_funCall); break; case FunctionType::Kind::Internal: + internalFunctionCall(_funCall); + break; case FunctionType::Kind::External: case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCall: @@ -525,6 +523,39 @@ void CHC::visitAssert(FunctionCall const& _funCall) m_context.addAssertion(m_error.currentValue() == previousError); } +void CHC::internalFunctionCall(FunctionCall const& _funCall) +{ + solAssert(m_currentContract, ""); + + auto const* function = functionCallToDefinition(_funCall); + if (function) + { + if (m_currentFunction && !m_currentFunction->isConstructor()) + m_callGraph[m_currentFunction].insert(function); + else + m_callGraph[m_currentContract].insert(function); + auto const* contract = function->annotation().contract; + + // Libraries can have constants as their "state" variables, + // so we need to ensure they were constructed correctly. + if (contract->isLibrary()) + m_context.addAssertion(interface(*contract)); + } + + auto previousError = m_error.currentValue(); + + m_context.addAssertion(predicate(_funCall)); + + connectBlocks( + m_currentBlock, + (m_currentFunction && !m_currentFunction->isConstructor()) ? summary(*m_currentFunction) : summary(*m_currentContract), + (m_error.currentValue() > 0) + ); + m_context.addAssertion(m_error.currentValue() == 0); + m_error.increaseIndex(); + m_context.addAssertion(m_error.currentValue() == previousError); +} + void CHC::unknownFunctionCall(FunctionCall const&) { /// Function calls are not handled at the moment, @@ -580,12 +611,7 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c bool CHC::shouldVisit(FunctionDefinition const& _function) const { - if ( - _function.isPublic() && - _function.isImplemented() - ) - return true; - return false; + return _function.isImplemented(); } void CHC::setCurrentBlock( @@ -634,29 +660,25 @@ vector CHC::stateSorts(ContractDefinition const& _contract) smt::SortPointer CHC::constructorSort() { - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); return make_shared( - vector{intSort} + m_stateSorts, - boolSort + vector{smt::SortProvider::intSort} + m_stateSorts, + smt::SortProvider::boolSort ); } smt::SortPointer CHC::interfaceSort() { - auto boolSort = make_shared(smt::Kind::Bool); return make_shared( m_stateSorts, - boolSort + smt::SortProvider::boolSort ); } smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) { - auto boolSort = make_shared(smt::Kind::Bool); return make_shared( stateSorts(_contract), - boolSort + smt::SortProvider::boolSort ); } @@ -673,8 +695,6 @@ smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) /// - 1 set of output variables smt::SortPointer CHC::sort(FunctionDefinition const& _function) { - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); vector inputSorts; for (auto const& var: _function.parameters()) inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); @@ -682,8 +702,8 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function) for (auto const& var: _function.returnParameters()) outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - vector{intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, - boolSort + vector{smt::SortProvider::intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, + smt::SortProvider::boolSort ); } @@ -695,13 +715,12 @@ smt::SortPointer CHC::sort(ASTNode const* _node) auto fSort = dynamic_pointer_cast(sort(*m_currentFunction)); solAssert(fSort, ""); - auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: m_currentFunction->localVariables()) varSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( fSort->domain + varSorts, - boolSort + smt::SortProvider::boolSort ); } @@ -710,16 +729,14 @@ smt::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractD auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); auto sorts = stateSorts(_contract); - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); vector inputSorts, outputSorts; for (auto const& var: _function.parameters()) inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); for (auto const& var: _function.returnParameters()) outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - vector{intSort} + sorts + inputSorts + sorts + outputSorts, - boolSort + vector{smt::SortProvider::intSort} + sorts + inputSorts + sorts + outputSorts, + smt::SortProvider::boolSort ); } @@ -919,6 +936,34 @@ smt::Expression CHC::predicate( return _block(_arguments); } +smt::Expression CHC::predicate(FunctionCall const& _funCall) +{ + auto const* function = functionCallToDefinition(_funCall); + if (!function) + return smt::Expression(true); + + m_error.increaseIndex(); + vector args{m_error.currentValue()}; + auto const* contract = function->annotation().contract; + + args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : currentStateVariables(); + args += symbolicArguments(_funCall); + for (auto const& var: m_stateVariables) + m_context.variable(*var)->increaseIndex(); + args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(); + + auto const& returnParams = function->returnParameters(); + for (auto param: returnParams) + if (m_context.knownVariable(*param)) + m_context.variable(*param)->increaseIndex(); + else + createVariable(*param); + for (auto const& var: function->returnParameters()) + args.push_back(m_context.variable(*var)->currentValue()); + + return (*m_summaries.at(contract).at(function))(args); +} + void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) { m_interface->addRule(_rule, _ruleName); diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 889c21b6a..4f601a2d5 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -76,6 +76,7 @@ private: void endVisit(Continue const& _node) override; void visitAssert(FunctionCall const& _funCall); + void internalFunctionCall(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); //@} @@ -164,6 +165,8 @@ private: smt::Expression predicate(smt::SymbolicFunctionVariable const& _block); /// @returns a predicate application over @param _arguments. smt::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); + /// @returns the summary predicate for the called function. + smt::Expression predicate(FunctionCall const& _funCall); /// @returns a predicate that defines a constructor summary. smt::Expression summary(ContractDefinition const& _contract); /// @returns a predicate that defines a function summary. diff --git a/libsolidity/formal/EncodingContext.cpp b/libsolidity/formal/EncodingContext.cpp index ff193d2e2..1f7b7a5e0 100644 --- a/libsolidity/formal/EncodingContext.cpp +++ b/libsolidity/formal/EncodingContext.cpp @@ -28,8 +28,8 @@ EncodingContext::EncodingContext(): m_thisAddress(make_unique("this", *this)) { auto sort = make_shared( - make_shared(Kind::Int), - make_shared(Kind::Int) + SortProvider::intSort, + SortProvider::intSort ); m_balances = make_unique(sort, "balances", *this); } diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 3b2f48816..0efaf90a9 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -673,7 +673,6 @@ void SMTEncoder::visitAssert(FunctionCall const& _funCall) auto const& args = _funCall.arguments(); solAssert(args.size() == 1, ""); solAssert(args.front()->annotation().type->category() == Type::Category::Bool, ""); - addPathImpliedExpression(expr(*args.front())); } void SMTEncoder::visitRequire(FunctionCall const& _funCall) diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 512be65bb..106ada820 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -17,6 +17,8 @@ #pragma once +#include + #include #include #include @@ -52,94 +54,6 @@ enum class CheckResult SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR }; -enum class Kind -{ - Int, - Bool, - Function, - Array, - Sort -}; - -struct Sort -{ - Sort(Kind _kind): - kind(_kind) {} - virtual ~Sort() = default; - virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } - - Kind const kind; -}; -using SortPointer = std::shared_ptr; - -struct FunctionSort: public Sort -{ - FunctionSort(std::vector _domain, SortPointer _codomain): - Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherFunction = dynamic_cast(&_other); - solAssert(_otherFunction, ""); - if (domain.size() != _otherFunction->domain.size()) - return false; - if (!std::equal( - domain.begin(), - domain.end(), - _otherFunction->domain.begin(), - [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } - )) - return false; - solAssert(codomain, ""); - solAssert(_otherFunction->codomain, ""); - return *codomain == *_otherFunction->codomain; - } - - std::vector domain; - SortPointer codomain; -}; - -struct ArraySort: public Sort -{ - /// _domain is the sort of the indices - /// _range is the sort of the values - ArraySort(SortPointer _domain, SortPointer _range): - Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherArray = dynamic_cast(&_other); - solAssert(_otherArray, ""); - solAssert(_otherArray->domain, ""); - solAssert(_otherArray->range, ""); - solAssert(domain, ""); - solAssert(range, ""); - return *domain == *_otherArray->domain && *range == *_otherArray->range; - } - - SortPointer domain; - SortPointer range; -}; - -struct SortSort: public Sort -{ - SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherSort = dynamic_cast(&_other); - solAssert(_otherSort, ""); - solAssert(_otherSort->inner, ""); - solAssert(inner, ""); - return *inner == *_otherSort->inner; - } - - SortPointer inner; -}; - // Forward declaration. SortPointer smtSort(Type const& _type); diff --git a/libsolidity/formal/Sorts.cpp b/libsolidity/formal/Sorts.cpp new file mode 100644 index 000000000..195048e56 --- /dev/null +++ b/libsolidity/formal/Sorts.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 . +*/ + + +#include + +using namespace std; + +namespace solidity::frontend::smt +{ + +shared_ptr const SortProvider::boolSort{make_shared(Kind::Bool)}; +shared_ptr const SortProvider::intSort{make_shared(Kind::Int)}; + +} diff --git a/libsolidity/formal/Sorts.h b/libsolidity/formal/Sorts.h new file mode 100644 index 000000000..d26644462 --- /dev/null +++ b/libsolidity/formal/Sorts.h @@ -0,0 +1,125 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace solidity::frontend::smt +{ + +enum class Kind +{ + Int, + Bool, + Function, + Array, + Sort +}; + +struct Sort +{ + Sort(Kind _kind): + kind(_kind) {} + virtual ~Sort() = default; + virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } + + Kind const kind; +}; +using SortPointer = std::shared_ptr; + +struct FunctionSort: public Sort +{ + FunctionSort(std::vector _domain, SortPointer _codomain): + Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherFunction = dynamic_cast(&_other); + solAssert(_otherFunction, ""); + if (domain.size() != _otherFunction->domain.size()) + return false; + if (!std::equal( + domain.begin(), + domain.end(), + _otherFunction->domain.begin(), + [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } + )) + return false; + solAssert(codomain, ""); + solAssert(_otherFunction->codomain, ""); + return *codomain == *_otherFunction->codomain; + } + + std::vector domain; + SortPointer codomain; +}; + +struct ArraySort: public Sort +{ + /// _domain is the sort of the indices + /// _range is the sort of the values + ArraySort(SortPointer _domain, SortPointer _range): + Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherArray = dynamic_cast(&_other); + solAssert(_otherArray, ""); + solAssert(_otherArray->domain, ""); + solAssert(_otherArray->range, ""); + solAssert(domain, ""); + solAssert(range, ""); + return *domain == *_otherArray->domain && *range == *_otherArray->range; + } + + SortPointer domain; + SortPointer range; +}; + +struct SortSort: public Sort +{ + SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherSort = dynamic_cast(&_other); + solAssert(_otherSort, ""); + solAssert(_otherSort->inner, ""); + solAssert(inner, ""); + return *inner == *_otherSort->inner; + } + + SortPointer inner; +}; + +/** Frequently used sorts.*/ +struct SortProvider +{ + static std::shared_ptr const boolSort; + static std::shared_ptr const intSort; +}; + +} diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index a2475b337..1d9655a9c 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -32,9 +32,9 @@ SortPointer smtSort(frontend::Type const& _type) switch (smtKind(_type.category())) { case Kind::Int: - return make_shared(Kind::Int); + return SortProvider::intSort; case Kind::Bool: - return make_shared(Kind::Bool); + return SortProvider::boolSort; case Kind::Function: { auto fType = dynamic_cast(&_type); @@ -45,10 +45,10 @@ SortPointer smtSort(frontend::Type const& _type) // 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(Kind::Bool); + returnSort = SortProvider::boolSort; else if (returnTypes.size() > 1) // Abstract sort. - returnSort = make_shared(Kind::Int); + returnSort = SortProvider::intSort; else returnSort = smtSort(*returnTypes.front()); return make_shared(parameterSorts, returnSort); @@ -65,20 +65,19 @@ SortPointer smtSort(frontend::Type const& _type) { auto stringLitType = dynamic_cast(&_type); solAssert(stringLitType, ""); - auto intSort = make_shared(Kind::Int); - return make_shared(intSort, intSort); + return make_shared(SortProvider::intSort, SortProvider::intSort); } else { solAssert(isArray(_type.category()), ""); auto arrayType = dynamic_cast(&_type); solAssert(arrayType, ""); - return make_shared(make_shared(Kind::Int), smtSortAbstractFunction(*arrayType->baseType())); + return make_shared(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType())); } } default: // Abstract case. - return make_shared(Kind::Int); + return SortProvider::intSort; } } @@ -93,7 +92,7 @@ vector smtSort(vector const& _types) SortPointer smtSortAbstractFunction(frontend::Type const& _type) { if (isFunction(_type.category())) - return make_shared(Kind::Int); + return SortProvider::intSort; return smtSort(_type); } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 8f285c883..549fc05e9 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -218,15 +218,28 @@ SymbolicArrayVariable::SymbolicArrayVariable( solAssert(isArray(m_type->category()), ""); } +SymbolicArrayVariable::SymbolicArrayVariable( + SortPointer _sort, + string _uniqueName, + EncodingContext& _context +): + SymbolicVariable(move(_sort), move(_uniqueName), _context) +{ + solAssert(m_sort->kind == Kind::Array, ""); +} + smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const { if (_targetType) + { + solAssert(m_originalType, ""); // StringLiterals are encoded as SMT arrays in the generic case, // but they can also be compared/assigned to fixed bytes, in which // case they'd need to be encoded as numbers. if (auto strType = dynamic_cast(m_originalType)) if (_targetType->category() == frontend::Type::Category::FixedBytes) return smt::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add))); + } return SymbolicVariable::currentValue(_targetType); } diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index be75931f2..e1c28a8b5 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -47,6 +47,8 @@ public: EncodingContext& _context ); + SymbolicVariable(SymbolicVariable&&) = default; + virtual ~SymbolicVariable() = default; virtual Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const; @@ -212,6 +214,13 @@ public: std::string _uniqueName, EncodingContext& _context ); + SymbolicArrayVariable( + SortPointer _sort, + std::string _uniqueName, + EncodingContext& _context + ); + + SymbolicArrayVariable(SymbolicArrayVariable&&) = default; Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override; }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 49b17cf33..fe0268fb1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -383,6 +384,15 @@ bool CompilerStack::analyze() noErrors = false; } + // Check that immutable variables are never read in c'tors and assigned + // exactly once + if (noErrors) + for (Source const* source: m_sourceOrder) + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + ImmutableValidator(m_errorReporter, *contract).analyze(); + if (noErrors) { // Control flow graph generator and analyzer. It can check for issues such as @@ -899,8 +909,7 @@ h256 const& CompilerStack::Source::swarmHash() const string const& CompilerStack::Source::ipfsUrl() const { if (ipfsUrlCached.empty()) - if (scanner->source().size() < 1024 * 256) - ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); + ipfsUrlCached = "dweb:/ipfs/" + util::ipfsHashBase58(scanner->source()); return ipfsUrlCached; } @@ -1373,10 +1382,7 @@ bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimen MetadataCBOREncoder encoder; if (m_metadataHash == MetadataHash::IPFS) - { - solAssert(_metadata.length() < 1024 * 256, "Metadata too large."); encoder.pushBytes("ipfs", util::ipfsHash(_metadata)); - } else if (m_metadataHash == MetadataHash::Bzzr1) encoder.pushBytes("bzzr1", util::bzzr1Hash(_metadata).asBytes()); else diff --git a/libsolidity/interface/DebugSettings.h b/libsolidity/interface/DebugSettings.h index 67c6d8810..34818889c 100644 --- a/libsolidity/interface/DebugSettings.h +++ b/libsolidity/interface/DebugSettings.h @@ -54,7 +54,7 @@ inline std::optional revertStringsFromString(std::string const& _ for (auto i: {RevertStrings::Default, RevertStrings::Strip, RevertStrings::Debug, RevertStrings::VerboseDebug}) if (revertStringsToString(i) == _str) return i; - return {}; + return std::nullopt; } } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 77e00df05..a97114e83 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -234,6 +234,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection) "wast", "wasm", "ewasm.wast", "ewasm.wasm", "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.immutableReferences", "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences", "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" @@ -309,13 +310,36 @@ Json::Value formatLinkReferences(std::map const& linkRefere return ret; } -Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap) +Json::Value formatImmutableReferences(map>> const& _immutableReferences) +{ + Json::Value ret(Json::objectValue); + + for (auto const& immutableReference: _immutableReferences) + { + auto const& [identifier, byteOffsets] = immutableReference.second; + Json::Value array(Json::arrayValue); + for (size_t byteOffset: byteOffsets) + { + Json::Value byteRange(Json::objectValue); + byteRange["start"] = Json::UInt(byteOffset); + byteRange["length"] = Json::UInt(32); // immutable references are currently always 32 bytes wide + array.append(byteRange); + } + ret[identifier] = array; + } + + return ret; +} + +Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap, bool _runtimeObject) { Json::Value output = Json::objectValue; output["object"] = _object.toHex(); output["opcodes"] = evmasm::disassemble(_object.bytecode); output["sourceMap"] = _sourceMap ? *_sourceMap : ""; output["linkReferences"] = formatLinkReferences(_object.linkReferences); + if (_runtimeObject) + output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences); return output; } @@ -982,19 +1006,21 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting )) evmData["bytecode"] = collectEVMObject( compilerStack.object(contractName), - compilerStack.sourceMapping(contractName) + compilerStack.sourceMapping(contractName), + false ); if (compilationSuccess && isArtifactRequested( _inputsAndSettings.outputSelection, file, name, - { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" }, + { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", "evm.deployedBytecode.immutableReferences" }, wildcardMatchesExperimental )) evmData["deployedBytecode"] = collectEVMObject( compilerStack.runtimeObject(contractName), - compilerStack.runtimeSourceMapping(contractName) + compilerStack.runtimeSourceMapping(contractName), + true ); if (!evmData.empty()) @@ -1081,7 +1107,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) { "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }, wildcardMatchesExperimental )) - output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get()); + output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get(), false); if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index edf43377f..2f23c8c22 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -730,8 +730,18 @@ ASTPointer Parser::parseVariableDeclaration( { if (_options.allowIndexed && token == Token::Indexed) isIndexed = true; - else if (token == Token::Constant) - constantness = VariableDeclaration::Constantness::Constant; + else if (token == Token::Constant || token == Token::Immutable) + { + if (constantness != VariableDeclaration::Constantness::Mutable) + parserError( + string("Constantness already set to ") + + (constantness == VariableDeclaration::Constantness::Constant ? "\"constant\"" : "\"immutable\"") + ); + else if (token == Token::Constant) + constantness = VariableDeclaration::Constantness::Constant; + else if (token == Token::Immutable) + constantness = VariableDeclaration::Constantness::Immutable; + } else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { if (location != VariableDeclaration::Location::Unspecified) diff --git a/libsolutil/IpfsHash.cpp b/libsolutil/IpfsHash.cpp index d6a511a24..95605cdc1 100644 --- a/libsolutil/IpfsHash.cpp +++ b/libsolutil/IpfsHash.cpp @@ -40,6 +40,21 @@ bytes varintEncoding(size_t _n) return encoded; } +bytes encodeByteArray(bytes const& _data) +{ + return bytes{0x0a} + varintEncoding(_data.size()) + _data; +} + +bytes encodeHash(bytes const& _data) +{ + return bytes{0x12, 0x20} + picosha2::hash256(_data); +} + +bytes encodeLinkData(bytes const& _data) +{ + return bytes{0x12} + varintEncoding(_data.size()) + _data; +} + string base58Encode(bytes const& _data) { static string const alphabet{"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}; @@ -53,36 +68,132 @@ string base58Encode(bytes const& _data) reverse(output.begin(), output.end()); return output; } + +struct Chunk +{ + Chunk() = default; + Chunk(bytes _hash, size_t _size, size_t _blockSize): + hash(std::move(_hash)), + size(_size), + blockSize(_blockSize) + {} + + bytes hash = {}; + size_t size = 0; + size_t blockSize = 0; +}; + +using Chunks = vector; + +Chunk combineLinks(Chunks& _links) +{ + bytes data = {}; + bytes lengths = {}; + Chunk chunk = {}; + for (Chunk& link: _links) + { + chunk.size += link.size; + chunk.blockSize += link.blockSize; + + data += encodeLinkData( + bytes {0x0a} + + varintEncoding(link.hash.size()) + + std::move(link.hash) + + bytes{0x12, 0x00, 0x18} + + varintEncoding(link.blockSize) + ); + + lengths += bytes{0x20} + varintEncoding(link.size); + } + + bytes blockData = data + encodeByteArray(bytes{0x08, 0x02, 0x18} + varintEncoding(chunk.size) + lengths); + + chunk.blockSize += blockData.size(); + chunk.hash = encodeHash(blockData); + + return chunk; +} + +Chunks buildNextLevel(Chunks& _currentLevel) +{ + size_t const maxChildNum = 174; + + Chunks nextLevel; + Chunks links; + + for (Chunk& chunk: _currentLevel) + { + links.emplace_back(std::move(chunk.hash), chunk.size, chunk.blockSize); + if (links.size() == maxChildNum) + { + nextLevel.emplace_back(combineLinks(links)); + links = {}; + } + } + if (!links.empty()) + nextLevel.emplace_back(combineLinks(links)); + + return nextLevel; +} + +/// Builds a tree starting from the bottom level where nodes are data nodes. +/// Data nodes should be calculated and passed as the only level in chunk levels +/// Each next level is calculated as following: +/// - Pick up to maxChildNum (174) nodes until a whole level is added, group them and pass to the node in the next level +/// - Do this until the current level has only one node, return the hash in that node +bytes groupChunksBottomUp(Chunks _currentLevel) +{ + // when we reach root it will be the only node in that level + while (_currentLevel.size() != 1) + _currentLevel = buildNextLevel(_currentLevel); + + // top level's only node stores the hash for file + return _currentLevel.front().hash; +} } bytes solidity::util::ipfsHash(string _data) { - assertThrow(_data.length() < 1024 * 256, DataTooLong, "IPFS hash for large (chunked) files not yet implemented."); + size_t const maxChunkSize = 1024 * 256; + size_t chunkCount = _data.length() / maxChunkSize + (_data.length() % maxChunkSize > 0 ? 1 : 0); + chunkCount = chunkCount == 0 ? 1 : chunkCount; - bytes lengthAsVarint = varintEncoding(_data.size()); + Chunks allChunks; - bytes protobufEncodedData; - // Type: File - protobufEncodedData += bytes{0x08, 0x02}; - if (!_data.empty()) + for (unsigned long chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++) { - // Data (length delimited bytes) - protobufEncodedData += bytes{0x12}; - protobufEncodedData += lengthAsVarint; - protobufEncodedData += asBytes(std::move(_data)); + bytes chunkBytes = asBytes( + _data.substr(chunkIndex * maxChunkSize, min(maxChunkSize, _data.length() - chunkIndex * maxChunkSize)) + ); + + bytes lengthAsVarint = varintEncoding(chunkBytes.size()); + + bytes protobufEncodedData; + // Type: File + protobufEncodedData += bytes{0x08, 0x02}; + if (!chunkBytes.empty()) + { + // Data (length delimited bytes) + protobufEncodedData += bytes{0x12}; + protobufEncodedData += lengthAsVarint; + protobufEncodedData += chunkBytes; + } + // filesize: length as varint + protobufEncodedData += bytes{0x18} + lengthAsVarint; + + // PBDag: + // Data: (length delimited bytes) + bytes blockData = encodeByteArray(protobufEncodedData); + + // Multihash: sha2-256, 256 bits + allChunks.emplace_back( + encodeHash(blockData), + chunkBytes.size(), + blockData.size() + ); } - // filesize: length as varint - protobufEncodedData += bytes{0x18} + lengthAsVarint; - // PBDag: - // Data: (length delimited bytes) - size_t protobufLength = protobufEncodedData.size(); - bytes blockData = bytes{0x0a} + varintEncoding(protobufLength) + std::move(protobufEncodedData); - // TODO Handle "large" files with multiple blocks - - // Multihash: sha2-256, 256 bits - bytes hash = bytes{0x12, 0x20} + picosha2::hash256(std::move(blockData)); - return hash; + return groupChunksBottomUp(std::move(allChunks)); } string solidity::util::ipfsHashBase58(string _data) diff --git a/libsolutil/vector_ref.h b/libsolutil/vector_ref.h index 3bb71f35e..46901a46e 100644 --- a/libsolutil/vector_ref.h +++ b/libsolutil/vector_ref.h @@ -44,7 +44,6 @@ public: std::vector toBytes() const { return std::vector(reinterpret_cast(m_data), reinterpret_cast(m_data) + m_count * sizeof(T)); } std::string toString() const { return std::string((char const*)m_data, ((char const*)m_data) + m_count * sizeof(T)); } - template explicit operator vector_ref() const { assert(m_count * sizeof(T) / sizeof(T2) * sizeof(T2) / sizeof(T) == m_count); return vector_ref(reinterpret_cast(m_data), m_count * sizeof(T) / sizeof(T2)); } operator vector_ref() const { return vector_ref(m_data, m_count); } T* data() const { return m_data; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 708e76339..758b11d92 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -324,6 +324,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() case Token::Byte: case Token::Bool: case Token::Address: + case Token::Var: { YulString literal{currentLiteral()}; if (m_dialect.builtin(literal)) @@ -513,6 +514,7 @@ YulString Parser::expectAsmIdentifier() case Token::Address: case Token::Bool: case Token::Identifier: + case Token::Var: break; default: expectToken(Token::Identifier); diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 7c0eca276..e64420bd9 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -203,6 +203,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const EthAssemblyAdapter adapter(assembly); compileEVM(adapter, false, m_optimiserSettings.optimizeStackAllocation); object.bytecode = make_shared(assembly.assemble()); + yulAssert(object.bytecode->immutableReferences.empty(), "Leftover immutables."); object.assembly = assembly.assemblyString(); object.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh new file mode 100644 index 000000000..1d21fe75c --- /dev/null +++ b/scripts/common_cmdline.sh @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------------ +# vim:ts=4:et +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016-2019 solidity contributors. +# ------------------------------------------------------------------------------ + +FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" +OLDARGS="--optimize --combined-json abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" +function compileFull() +{ + local expected_exit_code=0 + local expect_output=0 + if [[ $1 = '-e' ]]; then + expected_exit_code=1 + expect_output=1 + shift; + fi + if [[ $1 = '-w' ]]; then + expect_output=1 + shift; + fi + if [[ $1 = '-o' ]]; then + expect_output=2 + shift; + fi + local args=$FULLARGS + if [[ $1 = '-v' ]]; then + if (echo $2 | grep -Po '(?<=0.4.)\d+' >/dev/null); then + patch=$(echo $2 | grep -Po '(?<=0.4.)\d+') + if (( patch < 22 )); then + args=$OLDARGS + fi + fi + shift 2 + fi + + local files="$*" + local output + + local stderr_path=$(mktemp) + + set +e + "$SOLC" ${args} ${files} >/dev/null 2>"$stderr_path" + local exit_code=$? + local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path") + set -e + rm "$stderr_path" + + if [[ \ + ("$exit_code" -ne "$expected_exit_code" || \ + ( $expect_output -eq 0 && -n "$errors" ) || \ + ( $expect_output -ne 0 && $expected_exit_code -eq 0 && $expect_output -ne 2 && -z "$errors" )) + ]] + then + printError "Unexpected compilation result:" + printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" + printError "Was failure: $exit_code" + echo "$errors" + printError "While calling:" + echo "\"$SOLC\" $ARGS $files" + printError "Inside directory:" + pwd + false + fi +} diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh new file mode 100755 index 000000000..c56bdc6c9 --- /dev/null +++ b/scripts/docs_version_pragma_check.sh @@ -0,0 +1,187 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016 solidity contributors. +#------------------------------------------------------------------------------ + +# This script verifies that the examples compile with the oldest version mentioned in the pragma. +# It does not verify that it cannot be compiled with an older version +# and it also does not verify that it can be compiled with the newest version compatible with the pragma. + +set -e + +## GLOBAL VARIABLES + +REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) +SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build} +source "${REPO_ROOT}/scripts/common.sh" +source "${REPO_ROOT}/scripts/common_cmdline.sh" + +function versionGreater() +{ + v1=$1 + v2=$2 + ver1=( ${v1//./ } ) + ver2=( ${v2//./ } ) + + if (( ${ver1[0]} > ${ver2[0]} )) + then + return 0 + elif (( ${ver1[0]} == ${ver2[0]} )) && (( ${ver1[1]} > ${ver2[1]} )) + then + return 0 + elif (( ${ver1[0]} == ${ver2[0]} )) && (( ${ver1[1]} == ${ver2[1]} )) && (( ${ver1[2]} > ${ver2[2]} )) + then + return 0 + fi + return 1 +} + +function versionEqual() +{ + if [ "$1" == "$2" ] + then + return 0 + fi + return 1 +} + +function getAllAvailableVersions() +{ + allVersions=() + local allListedVersions=( $( + wget -q -O- https://ethereum.github.io/solc-bin/bin/list.txt | + grep -Po '(?<=soljson-v)\d+.\d+.\d+(?=\+commit)' | + sort -V + ) ) + for listed in "${allListedVersions[@]}" + do + if versionGreater "$listed" "0.4.10" + then + allVersions+=( $listed ) + fi + done +} + +function findMinimalVersion() +{ + local f=$1 + local greater=false + local pragmaVersion + + # Get minimum compiler version defined by pragma + if (grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f")" + sign=">=" + elif (grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f")" + sign="^" + elif (grep -Po '(?<=pragma solidity >)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity >)\d+.\d+.\d+' "$f")" + sign=">" + greater=true; + else + printError "No valid pragma statement in file. Skipping..." + return + fi + + version="" + for ver in "${allVersions[@]}" + do + if versionGreater "$ver" "$pragmaVersion" + then + minVersion="$ver" + break + elif ([ $greater == false ]) && versionEqual "$ver" "$pragmaVersion" + then + version="$ver" + break + fi + done + + if [ -z version ] + then + printError "No release $sign$pragmaVersion was listed in available releases!" + fi +} + +printTask "Verifying that all examples from the documentation have the correct version range..." +SOLTMPDIR=$(mktemp -d) +( + set -e + cd "$SOLTMPDIR" + "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + + getAllAvailableVersions + + for f in *.sol + do + # The contributors guide uses syntax tests, but we cannot + # really handle them here. + if grep -E 'DeclarationError:|// ----' "$f" >/dev/null + then + continue + fi + echo "$f" + + opts='' + # We expect errors if explicitly stated, or if imports + # are used (in the style guide) + if ( ! grep -E "This will not compile after" "$f" >/dev/null && \ + grep -E "This will not compile|import \"" "$f" >/dev/null ) + then + opts="-e" + fi + + # ignore warnings in this case + opts="$opts -o" + + findMinimalVersion $f + if [ -z "$version" ] + then + continue + fi + + opts="$opts -v $version" + + solc_bin="solc-$version" + echo "$solc_bin" + if [[ ! -f "$solc_bin" ]] + then + echo "Downloading release from github..." + if wget -q https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux >/dev/null + then + mv solc-static-linux $solc_bin + else + printError "No release $version was found on github!" + continue + fi + fi + + ln -sf "$solc_bin" "solc" + chmod a+x solc + + SOLC="$SOLTMPDIR/solc" + compileFull $opts "$SOLTMPDIR/$f" + done +) +rm -rf "$SOLTMPDIR" +echo "Done." \ No newline at end of file diff --git a/scripts/endToEndExtraction/create_traces.sh b/scripts/endToEndExtraction/create_traces.sh new file mode 100755 index 000000000..f167fee5e --- /dev/null +++ b/scripts/endToEndExtraction/create_traces.sh @@ -0,0 +1,22 @@ +BASE_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 || exit ; pwd -P )" + +mkdir -p build +cd build || exit +cmake ../../../ +make soltest +cd test/ || exit +echo "running soltest on 'semanticTests/extracted'..." +./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --testpath ${BASE_PATH}/../../test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/extracted-tests.trace +echo "running soltest on 'semanticTests/extracted'... done" + +cd $BASE_PATH || exit +git clone git@github.com:ethereum/solidity.git solidity-develop +cd solidity-develop || exit +mkdir -p build +cd build || exit +cmake .. +make soltest +cd test/ || exit +echo "running soltest on 'SolidityEndToEndTest'..." +./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --testpath ${BASE_PATH}/solidity-develop/test --no-smt --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages --show-metadata > ${BASE_PATH}/endToEndExtraction-tests.trace +echo "running soltest on 'SolidityEndToEndTest'... done" diff --git a/scripts/endToEndExtraction/remove-testcases.py b/scripts/endToEndExtraction/remove-testcases.py new file mode 100755 index 000000000..89f50d0e1 --- /dev/null +++ b/scripts/endToEndExtraction/remove-testcases.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# pylint: disable=consider-using-enumerate, import-error + +import re +import os +import sys +import getopt +import tempfile +from getkey import getkey + + +def parse_call(call): + function = '' + arguments = "" + results = "" + search = re.search(r'// (.*):(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL) + if search: + function = search.group(1) + arguments = search.group(2) + results = search.group(3) + if results.find("#") != -1: + results = results[:results.find("#")] + else: + search = re.search(r'// (.*)(.*)\s->\s(.*)', call, re.MULTILINE | re.DOTALL) + if search: + function = search.group(1) + arguments = search.group(2) + results = search.group(3) + if results.find("#") != -1: + results = results[:results.find("#")] + if function.find("wei") >= 0: + function = function[:function.find(",")] + return function.strip(), arguments.strip(), results.strip() + + +def colorize(left, right, id): + red = "\x1b[31m" + yellow = "\x1b[33m" + reset = "\x1b[0m" + colors = [red, yellow] + color = colors[id % len(colors)] + function, arguments, results = parse_call(right) + left = left.replace("compileAndRun", color + "compileAndRun" + reset) + right = right.replace("constructor", color + "constructor" + reset) + if function: + left = left.replace(function, color + function + reset) + right = right.replace(function, color + function + reset) + if left.find(function): + bottom = " " * (left.find(function) - 4) + right + else: + bottom = " " + right + return " " + left + "\n" + bottom # " {:<90} {:<90}\n{}".format(left, right, bottom) + + +def get_checks(content, sol_file_path): + constructors = [] + checks = [] + for line in content.split("\n"): + line = line.strip() + if line.startswith("compileAndRun"): + constructors.append(line) + if line.startswith("ABI_CHECK") or line.startswith("BOOST_REQUIRE"): + checks.append(line) + sol_file = open(sol_file_path, "r") + sol_constructors = [] + sol_checks = [] + inside_expectations = False + for line in sol_file.readlines(): + if line.startswith("// constructor()"): + sol_constructors.append(line) + elif inside_expectations and line.startswith("// "): + sol_checks.append(line) + if line.startswith("// ----"): + inside_expectations = True + sol_file.close() + if len(constructors) == len(sol_constructors) == 1: + checks.insert(0, constructors[0]) + sol_checks.insert(0, sol_constructors[0]) + return checks, sol_checks + + +def show_test(name, content, sol_file_path, current_test, test_count): + cpp_file = tempfile.NamedTemporaryFile(delete=False) + cpp_file.write(content.encode()) + cpp_file.close() + + os.system("clear") + print(str(current_test) + " / " + str(test_count) + " - " + name + "\n") + diff_env = os.getenv('DIFF', "/usr/local/bin/colordiff -a -d -w -y -W 200 ") + os.system(diff_env + " " + cpp_file.name + " " + sol_file_path) + os.unlink(cpp_file.name) + print("\n") + + checks, sol_checks = get_checks(content, sol_file_path) + + if len(checks) == len(sol_checks): + for i in range(0, len(checks)): + print(colorize(checks[i].strip(), sol_checks[i].strip(), i)) + else: + print("warning: check count not matching. this should not happen!") + + what = "" + print("\nContinue? (ENTER) Abort? (ANY OTHER KEY)") + while what != '\n': + what = getkey() + if what != '\n': + sys.exit(0) + print() + + +def get_tests(e2e_path): + tests = [] + for f in os.listdir(e2e_path): + if f.endswith(".sol"): + tests.append(f.replace(".sol", "")) + return tests + + +def process_input_file(e2e_path, input_file, interactive): + tests = get_tests(e2e_path) + cpp_file = open(input_file, "r") + inside_test = False + test_name = "" + inside_extracted_test = False + new_lines = 0 + count = 0 + test_content = "" + for line in cpp_file.readlines(): + test = re.search(r'BOOST_AUTO_TEST_CASE\((.*)\)', line, re.M | re.I) + if test: + test_name = test.group(1) + inside_test = True + inside_extracted_test = inside_test & (test_name in tests) + if inside_extracted_test: + count = count + 1 + + if interactive and inside_extracted_test: + test_content = test_content + line + + if not inside_extracted_test: + if line == "\n": + new_lines = new_lines + 1 + else: + new_lines = 0 + if not interactive and new_lines <= 1: + sys.stdout.write(line) + + if line == "}\n": + if interactive and inside_extracted_test: + show_test(test_name, test_content.strip(), e2e_path + "/" + test_name + ".sol", count, len(tests)) + test_content = "" + inside_test = False + cpp_file.close() + sys.stdout.flush() + + +def main(argv): + interactive = False + input_file = None + try: + opts, args = getopt.getopt(argv, "if:") + except getopt.GetoptError: + print("./remove-testcases.py [-i] [-f ]") + sys.exit(1) + + for opt, arg in opts: + if opt == '-i': + interactive = True + elif opt in '-f': + input_file = arg + + base_path = os.path.dirname(__file__) + + if not input_file: + input_file = base_path + "/../../test/libsolidity/SolidityEndToEndTest.cpp" + + e2e_path = base_path + "/../../test/libsolidity/semanticTests/extracted" + + process_input_file(e2e_path, input_file, interactive) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/scripts/endToEndExtraction/verify-testcases.py b/scripts/endToEndExtraction/verify-testcases.py new file mode 100755 index 000000000..87dc309d8 --- /dev/null +++ b/scripts/endToEndExtraction/verify-testcases.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# +# - SolidityEndToEndTest.trace was created with soltest with the following command on +# ./soltest --color_output=false --log_level=test_suite -t SolidityEndToEndTest/ -- --no-smt +# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > SolidityEndToEndTest.trace +# - a trace of the semantic tests can be created by using +# ./soltest --color_output=false --log_level=test_suite -t semanticTests/extracted/ -- --no-smt +# --evmonepath /Users/alex/evmone/lib/libevmone.dylib --show-messages > semanticTests.trace +# +# verify-testcases.py will compare both traces. If these traces are identical, the extracted tests where +# identical with the tests specified in SolidityEndToEndTest.cpp. +# +# pylint: disable=too-many-instance-attributes + +import re +import os +import sys +import getopt +import json + + +class Trace: + def __init__(self, kind, parameter): + self.kind = kind + self.parameter = parameter + self._input = "" + self._output = "" + self.value = "" + self.result = "" + self.gas = "" + + def get_input(self): + return self._input + + def set_input(self, input): + if self.kind == "create": + # remove cbor encoded metadata from bytecode + length = int(input[-4:], 16) * 2 + self._input = input[:len(input) - length - 4] + + def get_output(self): + return self._output + + def set_output(self, output): + if self.kind == "create": + # remove cbor encoded metadata from bytecode + length = int(output[-4:], 16) * 2 + self._output = output[:len(output) - length - 4] + + def __str__(self): + # we ignore the used gas + result = str( + "kind='" + self.kind + "' parameter='" + self.parameter + "' input='" + self._input + + "' output='" + self._output + "' value='" + self.value + "' result='" + self.result + "'" + ) + return result + + +class TestCase: + def __init__(self, name): + self.name = name + self.metadata = None + self.traces = [] + + def add_trace(self, kind, parameter): + trace = Trace(kind, parameter) + self.traces.append(trace) + return trace + + +class TraceAnalyser: + def __init__(self, file): + self.file = file + self.tests = {} + self.ready = False + + def analyse(self): + trace_file = open(self.file, "r") + trace = None + test_case = None + for line in trace_file.readlines(): + test = re.search(r'Entering test case "(.*)"', line, re.M | re.I) + if test: + test_name = test.group(1) + test_case = TestCase(test_name) + self.tests[test_name] = test_case + + metadata = re.search(r'\s*metadata:\s*(.*)$', line, re.M | re.I) + if metadata: + test_case.metadata = json.loads(metadata.group(1)) + del test_case.metadata["sources"] + del test_case.metadata["compiler"]["version"] + + create = re.search(r'CREATE\s*([a-fA-F0-9]*):', line, re.M | re.I) + if create: + trace = test_case.add_trace("create", create.group(1)) + + call = re.search(r'CALL\s*([a-fA-F0-9]*)\s*->\s*([a-fA-F0-9]*):', line, re.M | re.I) + if call: + trace = test_case.add_trace("call", call.group(1)) # + "->" + call.group(2)) + + if not create and not call: + self.parse_parameters(line, trace) + + trace_file.close() + + print(self.file + ":", len(self.tests), "test-cases.") + + self.ready = True + + @staticmethod + def parse_parameters(line, trace): + input = re.search(r'\s*in:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if input: + trace.input = input.group(1) + output = re.search(r'\s*out:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if output: + trace.output = output.group(1) + result = re.search(r'\s*result:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if result: + trace.result = result.group(1) + gas_used = re.search(r'\s*gas\sused:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if gas_used: + trace.gas = gas_used.group(1) + value = re.search(r'\s*value:\s*([a-fA-F0-9]*)', line, re.M | re.I) + if value: + trace.value = value.group(1) + + def diff(self, analyser): + if not self.ready: + self.analyse() + if not analyser.ready: + analyser.analyse() + + intersection = set(self.tests.keys()) & set(analyser.tests.keys()) + mismatches = set() + + for test_name in intersection: + left = self.tests[test_name] + right = analyser.tests[test_name] + if json.dumps(left.metadata) != json.dumps(right.metadata): + mismatches.add( + (test_name, "metadata where different: " + json.dumps(left.metadata) + " != " + json.dumps( + right.metadata))) + if len(left.traces) != len(right.traces): + mismatches.add((test_name, "trace count are different: " + str(len(left.traces)) + + " != " + str(len(right.traces)))) + else: + self.check_traces(test_name, left, right, mismatches) + + for mismatch in mismatches: + print(mismatch[0]) + print(mismatch[1]) + + print(len(intersection), "test-cases - ", len(mismatches), " mismatche(s)") + + def check_traces(self, test_name, left, right, mismatches): + for trace_id in range(0, len(left.traces)): + left_trace = left.traces[trace_id] + right_trace = right.traces[trace_id] + assert (left_trace.kind == right_trace.kind) + if str(left_trace) != str(right_trace): + mismatch_info = " " + str(left_trace) + "\n" + mismatch_info += " " + str(right_trace) + "\n" + mismatch_info += " " + for ch in range(0, len(str(left_trace))): + if ch < len(str(left_trace)) and ch < len(str(right_trace)): + if str(left_trace)[ch] != str(right_trace)[ch]: + mismatch_info += "|" + else: + mismatch_info += " " + else: + mismatch_info += "|" + mismatch_info += "\n" + mismatches.add((test_name, mismatch_info)) + + +def main(argv): + extracted_tests_trace_file = None + end_to_end_trace_file = None + try: + opts, args = getopt.getopt(argv, "s:e:") + except getopt.GetoptError: + print("verify-testcases.py [-s ] [-e ]") + sys.exit(2) + + for opt, arg in opts: + if opt in '-s': + extracted_tests_trace_file = arg + elif opt in '-e': + end_to_end_trace_file = arg + + base_path = os.path.dirname(__file__) + if not extracted_tests_trace_file: + extracted_tests_trace_file = base_path + "/extracted-tests.trace" + if not end_to_end_trace_file: + end_to_end_trace_file = base_path + "/endToEndExtraction-tests.trace" + + for f in [extracted_tests_trace_file, end_to_end_trace_file]: + if not os.path.isfile(f): + print("trace file '" + f + "' not found. aborting.") + sys.exit(1) + + if not os.path.isfile(extracted_tests_trace_file): + print("semantic trace file '" + extracted_tests_trace_file + "' not found. aborting.") + sys.exit(1) + + semantic_trace = TraceAnalyser(extracted_tests_trace_file) + end_to_end_trace = TraceAnalyser(end_to_end_trace_file) + + semantic_trace.diff(end_to_end_trace) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh new file mode 100755 index 000000000..97cbe4ab4 --- /dev/null +++ b/scripts/test_antlr_grammar.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +set -e + +ROOT_DIR="$(dirname "$0")"/.. +WORKDIR="${ROOT_DIR}/build/antlr" +ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" +ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar" +GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")" + +SGR_RESET="\033[0m" +SGR_BOLD="\033[1m" +SGR_GREEN="\033[32m" +SGR_RED="\033[31m" +SGR_BLUE="\033[34m" + +vt_cursor_up() { echo -ne "\033[A"; } +vt_cursor_begin_of_line() { echo -ne "\r"; } + +download_antlr4() +{ + if [[ ! -e "$ANTLR_JAR" ]] + then + curl -o "${ANTLR_JAR}" "${ANTLR_JAR_URI}" + fi +} + +prepare_workdir() +{ + mkdir -p "${ROOT_DIR}/build/deps" + mkdir -p "${WORKDIR}" + mkdir -p "${WORKDIR}/src" + mkdir -p "${WORKDIR}/target" +} + +prepare_workdir +download_antlr4 + +if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \ + [ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ] +then + echo "Creating parser" + # Create lexer/parser from grammar + java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/" + + # Compile lexer/parser sources + javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" +fi + +# Run tests +failed_count=0 +test_file() +{ + local SOL_FILE + SOL_FILE="$(readlink -m "${1}")" + local cur=${2} + local max=${3} + + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..." + local output + output=$( + java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + sourceUnit <"${SOL_FILE}" 2>&1 + ) + vt_cursor_up + vt_cursor_begin_of_line + if [[ "${output}" == "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi +} + +# we only want to use files that do not contain errors or multi-source files. +SOL_FILES=() +while IFS='' read -r line +do + SOL_FILES+=("$line") +done < <( + grep -riL -E \ + "^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \ + "${ROOT_DIR}/test/libsolidity/syntaxTests" \ + "${ROOT_DIR}/test/libsolidity/semanticTests" \ +) + +test_count=0 +for SOL_FILE in "${SOL_FILES[@]}" +do + test_count=$((test_count + 1)) + test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]} +done + +echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed." +exit ${failed_count} diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e91f6446c..90c2931c5 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -147,6 +148,7 @@ static string const g_strOptimizeYul = "optimize-yul"; static string const g_strOutputDir = "output-dir"; static string const g_strOverwrite = "overwrite"; static string const g_strRevertStrings = "revert-strings"; +static string const g_strStorageLayout = "storage-layout"; /// Possible arguments to for --revert-strings static set const g_revertStringsArgs @@ -207,6 +209,7 @@ static string const g_argOptimizeRuns = g_strOptimizeRuns; static string const g_argOutputDir = g_strOutputDir; static string const g_argSignatureHashes = g_strSignatureHashes; static string const g_argStandardJSON = g_strStandardJSON; +static string const g_argStorageLayout = g_strStorageLayout; static string const g_argStrictAssembly = g_strStrictAssembly; static string const g_argVersion = g_strVersion; static string const g_stdinFileName = g_stdinFileNameStr; @@ -231,7 +234,8 @@ static set const g_combinedJsonArgs g_strOpcodes, g_strSignatureHashes, g_strSrcMap, - g_strSrcMapRuntime + g_strSrcMapRuntime, + g_strStorageLayout }; /// Possible arguments to for --machine @@ -293,7 +297,8 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) g_argNatspecUser, g_argNatspecDev, g_argOpcodes, - g_argSignatureHashes + g_argSignatureHashes, + g_argStorageLayout }) if (_args.count(arg)) return true; @@ -433,6 +438,18 @@ void CommandLineInterface::handleABI(string const& _contract) sout() << "Contract JSON ABI" << endl << data << endl; } +void CommandLineInterface::handleStorageLayout(string const& _contract) +{ + if (!m_args.count(g_argStorageLayout)) + return; + + string data = jsonCompactPrint(m_compiler->storageLayout(_contract)); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); + else + sout() << "Contract Storage Layout:" << endl << data << endl; +} + void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { std::string argName; @@ -833,7 +850,8 @@ Allowed options)", (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain."); + (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables."); desc.add(outputComponents); po::options_description allOptions = desc; @@ -1276,6 +1294,8 @@ void CommandLineInterface::handleCombinedJSON() contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); + if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) + contractData[g_strStorageLayout] = jsonCompactPrint(m_compiler->storageLayout(contractName)); if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); @@ -1653,6 +1673,7 @@ void CommandLineInterface::outputCompilationResults() handleSignatureHashes(contract); handleMetadata(contract); handleABI(contract); + handleStorageLayout(contract); handleNatspec(true, contract); handleNatspec(false, contract); } // end of contracts iteration diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 417501907..f6972cf04 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -74,6 +74,7 @@ private: void handleNatspec(bool _natspecDev, std::string const& _contract); void handleGasEstimation(std::string const& _contract); void handleFormal(); + void handleStorageLayout(std::string const& _contract); /// Fills @a m_sourceCodes initially and @a m_redirects. bool readInputFilesAndConfigureRemappings(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3ea080686..d63d9aec5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,6 +13,8 @@ set(sources Metadata.h TestCase.cpp TestCase.h + TestCaseReader.cpp + TestCaseReader.h ) detect_stray_source_files("${sources}" ".") @@ -139,25 +141,37 @@ set(libyul_sources detect_stray_source_files("${libyul_sources}" "libyul/") set(yul_phaser_sources - yulPhaser/Common.h + yulPhaser/TestHelpers.h + yulPhaser/TestHelpers.cpp + yulPhaser/TestHelpersTest.cpp yulPhaser/Common.cpp - yulPhaser/CommonTest.cpp yulPhaser/Chromosome.cpp yulPhaser/FitnessMetrics.cpp + yulPhaser/AlgorithmRunner.cpp yulPhaser/GeneticAlgorithms.cpp + yulPhaser/Mutations.cpp + yulPhaser/PairSelections.cpp + yulPhaser/Phaser.cpp yulPhaser/Population.cpp yulPhaser/Program.cpp + yulPhaser/ProgramCache.cpp yulPhaser/Selections.cpp yulPhaser/SimulationRNG.cpp # FIXME: yul-phaser is not a library so I can't just add it to target_link_libraries(). # My current workaround is just to include its source files here but this introduces # unnecessary duplication. Create a library or find a way to reuse the list in both places. + ../tools/yulPhaser/AlgorithmRunner.cpp + ../tools/yulPhaser/Common.cpp ../tools/yulPhaser/Chromosome.cpp ../tools/yulPhaser/FitnessMetrics.cpp ../tools/yulPhaser/GeneticAlgorithms.cpp + ../tools/yulPhaser/Mutations.cpp + ../tools/yulPhaser/PairSelections.cpp + ../tools/yulPhaser/Phaser.cpp ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp + ../tools/yulPhaser/ProgramCache.cpp ../tools/yulPhaser/Selections.cpp ../tools/yulPhaser/SimulationRNG.cpp ) diff --git a/test/Common.cpp b/test/Common.cpp index 709840bbc..0ed7c9881 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -96,7 +96,8 @@ CommonOptions::CommonOptions(std::string _caption): ("optimize", po::bool_switch(&optimize), "enables optimization") ("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization") ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") - ("show-messages", po::bool_switch(&showMessages), "enables message output"); + ("show-messages", po::bool_switch(&showMessages), "enables message output") + ("show-metadata", po::bool_switch(&showMetadata), "enables metadata output"); } void CommonOptions::validate() const diff --git a/test/Common.h b/test/Common.h index 63437da13..a65c95539 100644 --- a/test/Common.h +++ b/test/Common.h @@ -50,6 +50,7 @@ struct CommonOptions: boost::noncopyable bool disableSMT = false; bool useABIEncoderV2 = false; bool showMessages = false; + bool showMetadata = false; langutil::EVMVersion evmVersion() const; diff --git a/test/CommonSyntaxTest.cpp b/test/CommonSyntaxTest.cpp index 6d72ce010..e3edc3fb0 100644 --- a/test/CommonSyntaxTest.cpp +++ b/test/CommonSyntaxTest.cpp @@ -56,37 +56,37 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end) } -CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) +CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): + EVMVersionRestrictedTestCase(_filename), + m_evmVersion(_evmVersion) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_sources = parseSourcesAndSettings(file); - - m_expectations = parseExpectations(file); + m_sources = m_reader.sources(); + m_expectations = parseExpectations(m_reader.stream()); } TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { parseAndAnalyze(); - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } -bool CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted) { - if (m_expectations != m_errorList) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); - return false; - } - return true; + if (m_expectations == m_errorList) + return TestResult::Success; + + printExpectationAndError(_stream, _linePrefix, _formatted); + return TestResult::Failure; +} + +void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) +{ + string nextIndentLevel = _linePrefix + " "; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); } void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const diff --git a/test/CommonSyntaxTest.h b/test/CommonSyntaxTest.h index 22acd5413..e761e1487 100644 --- a/test/CommonSyntaxTest.h +++ b/test/CommonSyntaxTest.h @@ -73,7 +73,8 @@ protected: bool _formatted = false ); - virtual bool printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); + TestResult conclude(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); + void printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false); static std::vector parseExpectations(std::istream& _stream); diff --git a/test/TestCase.cpp b/test/TestCase.cpp index 6b0a16e27..f952e40bd 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -18,16 +18,9 @@ #include #include -#include - -#include -#include #include -#include -#include #include - #include using namespace std; @@ -35,13 +28,14 @@ using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::test; -void TestCase::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool) +void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const bool) { - if (m_validatedSettings.empty()) + auto& settings = m_reader.settings(); + if (settings.empty()) return; _stream << _linePrefix << "// ====" << endl; - for (auto const& setting: m_validatedSettings) + for (auto const& setting: settings) _stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; } @@ -53,108 +47,12 @@ bool TestCase::isTestFilename(boost::filesystem::path const& _filename) !boost::starts_with(_filename.string(), "."); } -void TestCase::validateSettings() -{ - if (!m_settings.empty()) - throw runtime_error( - "Unknown setting(s): " + - util::joinHumanReadable(m_settings | boost::adaptors::map_keys) - ); -} - bool TestCase::shouldRun() { + m_reader.ensureAllSettingsRead(); return m_shouldRun; } -pair, size_t> TestCase::parseSourcesAndSettingsWithLineNumbers(istream& _stream) -{ - map sources; - string currentSourceName; - string currentSource; - string line; - size_t lineNumber = 1; - static string const sourceDelimiterStart("==== Source:"); - static string const sourceDelimiterEnd("===="); - static string const comment("// "); - static string const settingsDelimiter("// ===="); - static string const delimiter("// ----"); - bool sourcePart = true; - while (getline(_stream, line)) - { - lineNumber++; - - if (boost::algorithm::starts_with(line, delimiter)) - break; - else if (boost::algorithm::starts_with(line, settingsDelimiter)) - sourcePart = false; - else if (sourcePart) - { - if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd)) - { - if (!(currentSourceName.empty() && currentSource.empty())) - sources[currentSourceName] = std::move(currentSource); - currentSource = {}; - currentSourceName = boost::trim_copy(line.substr( - sourceDelimiterStart.size(), - line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size() - )); - if (sources.count(currentSourceName)) - throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."); - } - else - currentSource += line + "\n"; - } - else if (boost::algorithm::starts_with(line, comment)) - { - size_t colon = line.find(':'); - if (colon == string::npos) - throw runtime_error(string("Expected \":\" inside setting.")); - string key = line.substr(comment.size(), colon - comment.size()); - string value = line.substr(colon + 1); - boost::algorithm::trim(key); - boost::algorithm::trim(value); - m_settings[key] = value; - } - else - throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source.")); - } - sources[currentSourceName] = currentSource; - return {sources, lineNumber}; -} - -map TestCase::parseSourcesAndSettings(istream& _stream) -{ - return get<0>(parseSourcesAndSettingsWithLineNumbers(_stream)); -} - -pair TestCase::parseSourceAndSettingsWithLineNumbers(istream& _stream) -{ - auto [sourceMap, lineOffset] = parseSourcesAndSettingsWithLineNumbers(_stream); - if (sourceMap.size() != 1) - BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources.")); - return {std::move(sourceMap.begin()->second), lineOffset}; -} - -string TestCase::parseSourceAndSettings(istream& _stream) -{ - return parseSourceAndSettingsWithLineNumbers(_stream).first; -} - -string TestCase::parseSimpleExpectations(std::istream& _file) -{ - string result; - string line; - while (getline(_file, line)) - if (boost::algorithm::starts_with(line, "// ")) - result += line.substr(3) + "\n"; - else if (line == "//") - result += "\n"; - else - BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \".")); - return result; -} - void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c) { if (_it == _end || *_it != _c) @@ -162,18 +60,11 @@ void TestCase::expect(string::iterator& _it, string::iterator _end, string::valu ++_it; } -void EVMVersionRestrictedTestCase::validateSettings() +EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filename): + TestCase(_filename) { - if (!m_settings.count("EVMVersion")) - return; - - string versionString = m_settings["EVMVersion"]; - m_validatedSettings["EVMVersion"] = versionString; - m_settings.erase("EVMVersion"); - - TestCase::validateSettings(); - - if (versionString.empty()) + string versionString = m_reader.stringSetting("EVMVersion", "any"); + if (versionString == "any") return; string comparator; diff --git a/test/TestCase.h b/test/TestCase.h index d6afff8b8..0add6947b 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -17,16 +17,13 @@ #pragma once +#include + #include #include -#include -#include -#include #include -#include -#include namespace solidity::frontend::test { @@ -60,31 +57,27 @@ public: /// If @arg _formatted is true, color-coding may be used to indicate /// error locations in the contract, if applicable. virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0; - /// Outputs the updated settings. - virtual void printUpdatedSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false); + /// Outputs settings. + virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false); /// Outputs test expectations to @arg _stream that match the actual results of the test. /// Each line of output is prefixed with @arg _linePrefix. virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0; static bool isTestFilename(boost::filesystem::path const& _filename); - /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. - /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). - virtual void validateSettings(); - /// Returns true, if the test case is supported in the current environment and false /// otherwise which causes this test to be skipped. /// This might check e.g. for restrictions on the EVM version. + /// The function throws an exception if there are unread settings. bool shouldRun(); protected: - std::pair, std::size_t> parseSourcesAndSettingsWithLineNumbers(std::istream& _file); - std::map parseSourcesAndSettings(std::istream& _file); - std::pair parseSourceAndSettingsWithLineNumbers(std::istream& _file); - std::string parseSourceAndSettings(std::istream& _file); - static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c); + // Used by ASTJSONTest, the only TestCase class with a custom parser of the test files. + TestCase() = default; - static std::string parseSimpleExpectations(std::istream& _file); + TestCase(std::string const& _filename): m_reader(_filename) {} + + static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c); template static void skipWhitespace(IteratorType& _it, IteratorType _end) @@ -100,18 +93,14 @@ protected: ++_it; } - /// Parsed settings. - std::map m_settings; - /// Updated settings after validation. - std::map m_validatedSettings; - + TestCaseReader m_reader; bool m_shouldRun = true; }; class EVMVersionRestrictedTestCase: public TestCase { -public: - void validateSettings() override; +protected: + EVMVersionRestrictedTestCase(std::string const& _filename); }; } diff --git a/test/TestCaseReader.cpp b/test/TestCaseReader.cpp new file mode 100644 index 000000000..0f49a6401 --- /dev/null +++ b/test/TestCaseReader.cpp @@ -0,0 +1,164 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::frontend::test; + +TestCaseReader::TestCaseReader(string const& _filename): + m_file(_filename) +{ + if (!m_file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\".")); + m_file.exceptions(ios::badbit); + + tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_file); + m_unreadSettings = m_settings; +} + +string const& TestCaseReader::source() +{ + if (m_sources.size() != 1) + BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources.")); + return m_sources.begin()->second; +} + +string TestCaseReader::simpleExpectations() +{ + return parseSimpleExpectations(m_file); +} + +bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue) +{ + if (m_settings.count(_name) == 0) + return _defaultValue; + + m_unreadSettings.erase(_name); + string value = m_settings.at(_name); + if (value == "false") + return false; + if (value == "true") + return true; + + BOOST_THROW_EXCEPTION(runtime_error("Invalid Boolean value: " + value + ".")); +} + +size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue) +{ + if (m_settings.count(_name) == 0) + return _defaultValue; + + m_unreadSettings.erase(_name); + + static_assert(sizeof(unsigned long) <= sizeof(size_t)); + return stoul(m_settings.at(_name)); +} + +string TestCaseReader::stringSetting(string const& _name, string const& _defaultValue) +{ + if (m_settings.count(_name) == 0) + return _defaultValue; + + m_unreadSettings.erase(_name); + return m_settings.at(_name); +} + +void TestCaseReader::ensureAllSettingsRead() const +{ + if (!m_unreadSettings.empty()) + throw runtime_error( + "Unknown setting(s): " + + util::joinHumanReadable(m_unreadSettings | boost::adaptors::map_keys) + ); +} + +pair, size_t> TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream) +{ + map sources; + string currentSourceName; + string currentSource; + string line; + size_t lineNumber = 1; + static string const sourceDelimiterStart("==== Source:"); + static string const sourceDelimiterEnd("===="); + static string const comment("// "); + static string const settingsDelimiter("// ===="); + static string const delimiter("// ----"); + bool sourcePart = true; + while (getline(_stream, line)) + { + lineNumber++; + + if (boost::algorithm::starts_with(line, delimiter)) + break; + else if (boost::algorithm::starts_with(line, settingsDelimiter)) + sourcePart = false; + else if (sourcePart) + { + if (boost::algorithm::starts_with(line, sourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd)) + { + if (!(currentSourceName.empty() && currentSource.empty())) + sources[currentSourceName] = std::move(currentSource); + currentSource = {}; + currentSourceName = boost::trim_copy(line.substr( + sourceDelimiterStart.size(), + line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size() + )); + if (sources.count(currentSourceName)) + throw runtime_error("Multiple definitions of test source \"" + currentSourceName + "\"."); + } + else + currentSource += line + "\n"; + } + else if (boost::algorithm::starts_with(line, comment)) + { + size_t colon = line.find(':'); + if (colon == string::npos) + throw runtime_error(string("Expected \":\" inside setting.")); + string key = line.substr(comment.size(), colon - comment.size()); + string value = line.substr(colon + 1); + boost::algorithm::trim(key); + boost::algorithm::trim(value); + m_settings[key] = value; + } + else + throw runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source.")); + } + sources[currentSourceName] = currentSource; + return { sources, lineNumber }; +} + +string TestCaseReader::parseSimpleExpectations(istream& _file) +{ + string result; + string line; + while (getline(_file, line)) + if (boost::algorithm::starts_with(line, "// ")) + result += line.substr(3) + "\n"; + else if (line == "//") + result += "\n"; + else + BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \".")); + return result; +} diff --git a/test/TestCaseReader.h b/test/TestCaseReader.h new file mode 100644 index 000000000..5ab226826 --- /dev/null +++ b/test/TestCaseReader.h @@ -0,0 +1,59 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include + +#pragma once + +namespace solidity::frontend::test +{ +/** + * A reader for test case data file, which parses source, settings and (optionally) simple expectations. + */ +class TestCaseReader +{ +public: + TestCaseReader() = default; + explicit TestCaseReader(std::string const& _filename); + + std::map const& sources() { return m_sources; } + std::string const& source(); + std::size_t lineNumber() { return m_lineNumber; } + std::map const& settings() { return m_settings; } + std::ifstream& stream() { return m_file; } + + std::string simpleExpectations(); + + bool boolSetting(std::string const& _name, bool _defaultValue); + size_t sizetSetting(std::string const& _name, size_t _defaultValue); + std::string stringSetting(std::string const& _name, std::string const& _defaultValue); + + void ensureAllSettingsRead() const; + +private: + std::pair, std::size_t> parseSourcesAndSettingsWithLineNumber(std::istream& _file); + static std::string parseSimpleExpectations(std::istream& _file); + + std::ifstream m_file; + std::map m_sources; + std::size_t m_lineNumber = 0; + std::map m_settings; + std::map m_unreadSettings; ///< tracks which settings are left unread +}; +} diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 3137a5085..fd7bca132 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -94,7 +94,6 @@ int registerTests( { stringstream errorStream; auto testCase = _testCaseCreator(config); - testCase->validateSettings(); if (testCase->shouldRun()) switch (testCase->run(errorStream)) { @@ -188,7 +187,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) if (solidity::test::CommonOptions::get().disableSMT) removeTestSuite("SMTChecker"); - return 0; + return nullptr; } // BOOST_TEST_DYN_LINK should be defined if user want to link against shared boost test library diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 393f3da8b..9001c02f4 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -33,6 +33,7 @@ set -e REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build} source "${REPO_ROOT}/scripts/common.sh" +source "${REPO_ROOT}/scripts/common_cmdline.sh" case "$OSTYPE" in msys) @@ -45,6 +46,7 @@ case "$OSTYPE" in SOLC="$REPO_ROOT/${SOLIDITY_BUILD_DIR}/solc/solc" ;; esac +echo "${SOLC}" INTERACTIVE=true if ! tty -s || [ "$CI" ] @@ -52,8 +54,6 @@ then INTERACTIVE="" fi -FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" - # extend stack size in case we run via ASAN if [[ -n "${CIRCLECI}" ]] || [[ -n "$CI" ]]; then ulimit -s 16384 @@ -62,52 +62,6 @@ fi ## FUNCTIONS -function compileFull() -{ - local expected_exit_code=0 - local expect_output=0 - if [[ $1 = '-e' ]] - then - expected_exit_code=1 - expect_output=1 - shift; - fi - if [[ $1 = '-w' ]] - then - expect_output=1 - shift; - fi - - local files="$*" - local output - - local stderr_path=$(mktemp) - - set +e - "$SOLC" $FULLARGS $files >/dev/null 2>"$stderr_path" - local exit_code=$? - local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|^ +--> |^ +\||^[0-9]+ +\|' < "$stderr_path") - set -e - rm "$stderr_path" - - if [[ \ - "$exit_code" -ne "$expected_exit_code" || \ - ( $expect_output -eq 0 && -n "$errors" ) || \ - ( $expect_output -ne 0 && -z "$errors" ) \ - ]] - then - printError "Unexpected compilation result:" - printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" - printError "Was failure: $exit_code" - echo "$errors" - printError "While calling:" - echo "\"$SOLC\" $FULLARGS $files" - printError "Inside directory:" - pwd - false - fi -} - function ask_expectation_update() { if [ $INTERACTIVE ] @@ -350,6 +304,10 @@ SOLTMPDIR=$(mktemp -d) then opts="$opts -w" fi + if grep "This may report a warning" "$f" >/dev/null + then + opts="$opts -o" + fi compileFull $opts "$SOLTMPDIR/$f" done ) diff --git a/test/cmdlineTests/standard_immutable_references/input.json b/test/cmdlineTests/standard_immutable_references/input.json new file mode 100644 index 000000000..15213be74 --- /dev/null +++ b/test/cmdlineTests/standard_immutable_references/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": { + "a.sol": { + "content": "contract A { uint256 immutable x = 1; function f() public view returns (uint256) { return x; } }" + } + }, + "settings": { + "evmVersion": "petersburg", + "outputSelection": { + "*": { + "A": [ + "evm.deployedBytecode.immutableReferences" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_immutable_references/output.json b/test/cmdlineTests/standard_immutable_references/output.json new file mode 100644 index 000000000..2788a8e73 --- /dev/null +++ b/test/cmdlineTests/standard_immutable_references/output.json @@ -0,0 +1,2 @@ +{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"3":[{"length":32,"start":77}]},"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"0:96:0:-:0;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;0:96:0;;;;;;;;;;;;;;;;12:1:-1;9;2:12;38:56:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;72:7;90:1;83:8;;38:56;:::o"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 82e974025..cb89a6911 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -20,6 +20,7 @@ object \"C_6\" { function fun_f_5() { + } } @@ -69,6 +70,7 @@ object \"C_6\" { function fun_f_5() { + } function shift_right_224_unsigned(value) -> newValue { diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index fc88acf50..24a2a10aa 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -35,11 +35,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } object \"C_10_deployed\" { code { @@ -131,6 +138,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave @@ -147,6 +157,10 @@ object \"C_10\" { } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 428c7911e..8028e5dd6 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -23,11 +23,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() + vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave } + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -88,6 +95,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() + vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave @@ -100,6 +110,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 4a348343b..a9588d145 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -27,6 +27,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0x61626364 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) leave @@ -40,6 +43,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -104,6 +111,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0x61626364 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) leave @@ -124,6 +134,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 4302701bd..552712154 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -39,11 +39,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } object \"C_10_deployed\" { code { @@ -139,6 +146,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave @@ -155,6 +165,10 @@ object \"C_10\" { } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 0b92fceea..48c1146fa 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -27,6 +27,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0xaabbccdd vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) leave @@ -40,6 +43,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -104,6 +111,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0xaabbccdd vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) leave @@ -124,6 +134,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } } } diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 2d7057f7c..a402101c4 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -61,6 +61,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) Assembly _subAsm; auto sub_asm = make_shared("lorem ipsum", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); + // PushImmutable + _subAsm.appendImmutable("someImmutable"); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); @@ -86,6 +88,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) _assembly.pushSubroutineOffset(size_t(sub.data())); // PushDeployTimeAddress _assembly.append(PushDeployTimeAddress); + // AssignImmutable. + // Note that since there is no reference to "someOtherImmutable", this will compile to a simple POP in the hex output. + _assembly.appendImmutableAssignment("someOtherImmutable"); + _assembly.append(u256(2)); + _assembly.appendImmutableAssignment("someImmutable"); // Operation _assembly.append(Instruction::STOP); _assembly.appendAuxiliaryDataToEnd(bytes{0x42, 0x66}); @@ -95,8 +102,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) BOOST_CHECK_EQUAL( _assembly.assemble().toHex(), - "5b6001600220604673__$bf005014d9d0f534b8fcb268bd84c491a2$__" - "600056603e6001603d73000000000000000000000000000000000000000000fe" + "5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__" + "60005660676022604573000000000000000000000000000000000000000050" + "60028060015250" + "00fe" + "7f0000000000000000000000000000000000000000000000000000000000000000" "fe010203044266eeaa" ); BOOST_CHECK_EQUAL( @@ -111,12 +121,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) " dataSize(sub_0)\n" " dataOffset(sub_0)\n" " deployTimeAddress()\n" + " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " 0x02\n" + " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " stop\n" "stop\n" "data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n" "\n" "sub_0: assembly {\n" " /* \"sub.asm\":6:8 */\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " invalid\n" "}\n" "\n" @@ -138,9 +152,104 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}" - "],\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}]}," - "\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + "],\".data\":{\"0\":{\".code\":[" + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}" + "]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + ); +} + +BOOST_AUTO_TEST_CASE(immutable) +{ + map indices = { + { "root.asm", 0 }, + { "sub.asm", 1 } + }; + Assembly _assembly; + auto root_asm = make_shared("lorem ipsum", "root.asm"); + _assembly.setSourceLocation({1, 3, root_asm}); + + Assembly _subAsm; + auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + _subAsm.setSourceLocation({6, 8, sub_asm}); + _subAsm.appendImmutable("someImmutable"); + _subAsm.appendImmutable("someOtherImmutable"); + _subAsm.appendImmutable("someImmutable"); + shared_ptr _subAsmPtr = make_shared(_subAsm); + + _assembly.append(u256(42)); + _assembly.appendImmutableAssignment("someImmutable"); + _assembly.append(u256(23)); + _assembly.appendImmutableAssignment("someOtherImmutable"); + + auto sub = _assembly.appendSubroutine(_subAsmPtr); + _assembly.pushSubroutineOffset(size_t(sub.data())); + + checkCompilation(_assembly); + + BOOST_CHECK_EQUAL( + _assembly.assemble().toHex(), + // root.asm + // assign "someImmutable" + "602a" // PUSH1 42 - value for someImmutable + "80" // DUP1 + "6001" // PUSH1 1 - offset of first someImmutable in sub_0 + "52" // MSTORE + "80" // DUP1 + "6043" // PUSH1 67 - offset of second someImmutable in sub_0 + "52" // MSTORE + "50" // POP + // assign "someOtherImmutable" + "6017" // PUSH1 23 - value for someOtherImmutable + "80" // DUP1 + "6022" // PUSH1 34 - offset of someOtherImmutable in sub_0 + "52" // MSTORE + "50" // POP + "6063" // PUSH1 0x63 - dataSize(sub_0) + "6017" // PUSH1 0x17 - dataOffset(sub_0) + "fe" // INVALID + // end of root.asm + // sub.asm + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 1 + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someOtherImmutable - data at offset 34 + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 67 + ); + BOOST_CHECK_EQUAL( + _assembly.assemblyString(), + " /* \"root.asm\":1:3 */\n" + " 0x2a\n" + " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " 0x17\n" + " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " dataSize(sub_0)\n" + " dataOffset(sub_0)\n" + "stop\n" + "\n" + "sub_0: assembly {\n" + " /* \"sub.asm\":6:8 */\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " immutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + "}\n" + ); + BOOST_CHECK_EQUAL( + util::jsonCompactPrint(_assembly.assemblyJSON(indices)), + "{\".code\":[" + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"17\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}" + "],\".data\":{\"0\":{\".code\":[" + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}" + "]}}}" ); } diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 4d93f4e76..782b687fc 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -112,6 +112,44 @@ namespace BOOST_AUTO_TEST_SUITE(Optimiser) +BOOST_AUTO_TEST_CASE(cse_push_immutable_same) +{ + AssemblyItem pushImmutable{PushImmutable, 0x1234}; + checkCSE({pushImmutable, pushImmutable}, {pushImmutable, Instruction::DUP1}); +} + +BOOST_AUTO_TEST_CASE(cse_push_immutable_different) +{ + AssemblyItems input{{PushImmutable, 0x1234},{PushImmutable, 0xABCD}}; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_assign_immutable) +{ + { + AssemblyItems input{u256(0x42), {AssignImmutable, 0x1234}}; + checkCSE(input, input); + } + { + AssemblyItems input{{AssignImmutable, 0x1234}}; + checkCSE(input, input); + } +} + + +BOOST_AUTO_TEST_CASE(cse_assign_immutable_breaks) +{ + AssemblyItems input = addDummyLocations(AssemblyItems{ + u256(0x42), + {AssignImmutable, 0x1234}, + Instruction::ORIGIN + }); + + evmasm::CommonSubexpressionEliminator cse{evmasm::KnownState()}; + // Make sure CSE breaks after AssignImmutable. + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.begin() + 2); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { evmasm::KnownState state; @@ -798,6 +836,68 @@ BOOST_AUTO_TEST_CASE(block_deduplicator) BOOST_CHECK_EQUAL(pushTags.size(), 2); } +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_same) +{ + AssemblyItems blocks{ + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP + }; + + AssemblyItems input = AssemblyItems{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + } + blocks; + AssemblyItems output = AssemblyItems{ + AssemblyItem(PushTag, 1), + AssemblyItem(PushTag, 1), + } + blocks; + BlockDeduplicator dedup(input); + dedup.deduplicate(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_value) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(23), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP + }; + BlockDeduplicator dedup(input); + BOOST_CHECK(!dedup.deduplicate()); +} + +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(42), + AssemblyItem{AssignImmutable, 0xABCD}, + Instruction::JUMP + }; + BlockDeduplicator dedup(input); + BOOST_CHECK(!dedup.deduplicate()); +} + BOOST_AUTO_TEST_CASE(block_deduplicator_loops) { AssemblyItems input{ diff --git a/test/libsolidity/ABIJsonTest.cpp b/test/libsolidity/ABIJsonTest.cpp index 1da0193ab..e7a0cce6d 100644 --- a/test/libsolidity/ABIJsonTest.cpp +++ b/test/libsolidity/ABIJsonTest.cpp @@ -36,15 +36,11 @@ using namespace solidity::util; using namespace solidity::frontend; using namespace solidity::frontend::test; -ABIJsonTest::ABIJsonTest(string const& _filename) +ABIJsonTest::ABIJsonTest(string const& _filename): + TestCase(_filename) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult ABIJsonTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index 0aa3d2984..501184c9c 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -54,7 +54,7 @@ "storageLocation": "memory", "typeDescriptions": { - "typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr", + "typeIdentifier": "t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr", "typeString": "uint256[][]" }, "typeName": diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index b1b0d56f2..694839b99 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -36,35 +36,14 @@ using namespace std; namespace fs = boost::filesystem; using namespace boost::unit_test; -GasTest::GasTest(string const& _filename) +GasTest::GasTest(string const& _filename): + TestCase(_filename) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - - if (m_settings.count("optimize")) - { - m_optimise = true; - m_validatedSettings["optimize"] = "true"; - m_settings.erase("optimize"); - } - if (m_settings.count("optimize-yul")) - { - m_optimiseYul = true; - m_validatedSettings["optimize-yul"] = "true"; - m_settings.erase("optimize-yul"); - } - if (m_settings.count("optimize-runs")) - { - m_optimiseRuns = stoul(m_settings["optimize-runs"]); - m_validatedSettings["optimize-runs"] = m_settings["optimize-runs"]; - m_settings.erase("optimize-runs"); - } - - parseExpectations(file); + m_source = m_reader.source(); + m_optimise = m_reader.boolSetting("optimize", false); + m_optimiseYul = m_reader.boolSetting("optimize-yul", false); + m_optimiseRuns = m_reader.sizetSetting("optimize-runs", 200); + parseExpectations(m_reader.stream()); } void GasTest::parseExpectations(std::istream& _stream) diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp index 82c2b446b..a1c23662a 100644 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ b/test/libsolidity/SMTCheckerJSONTest.cpp @@ -17,14 +17,18 @@ #include #include + +#include #include #include #include + #include #include #include #include #include + #include #include #include @@ -50,6 +54,9 @@ SMTCheckerJSONTest::SMTCheckerJSONTest(string const& _filename, langutil::EVMVer !m_smtResponses.isObject() ) BOOST_THROW_EXCEPTION(runtime_error("Invalid JSON file.")); + + if (ModelChecker::availableSolvers().none()) + m_shouldRun = false; } TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) @@ -128,7 +135,7 @@ TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _li } } - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } vector SMTCheckerJSONTest::hashesFromJson(Json::Value const& _jsonObj, string const& _auxInput, string const& _smtlib) diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 912349182..28c98ffab 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -28,22 +28,17 @@ using namespace solidity::frontend::test; SMTCheckerTest::SMTCheckerTest(string const& _filename, langutil::EVMVersion _evmVersion): SyntaxTest(_filename, _evmVersion) { - if (m_settings.count("SMTSolvers")) - { - auto const& choice = m_settings.at("SMTSolvers"); - if (choice == "any") - m_enabledSolvers = smt::SMTSolverChoice::All(); - else if (choice == "z3") - m_enabledSolvers = smt::SMTSolverChoice::Z3(); - else if (choice == "cvc4") - m_enabledSolvers = smt::SMTSolverChoice::CVC4(); - else if (choice == "none") - m_enabledSolvers = smt::SMTSolverChoice::None(); - else - BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice.")); - } - else + auto const& choice = m_reader.stringSetting("SMTSolvers", "any"); + if (choice == "any") m_enabledSolvers = smt::SMTSolverChoice::All(); + else if (choice == "z3") + m_enabledSolvers = smt::SMTSolverChoice::Z3(); + else if (choice == "cvc4") + m_enabledSolvers = smt::SMTSolverChoice::CVC4(); + else if (choice == "none") + m_enabledSolvers = smt::SMTSolverChoice::None(); + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT solver choice.")); auto available = ModelChecker::availableSolvers(); if (!available.z3) @@ -62,5 +57,5 @@ TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePr parseAndAnalyze(); filterObtainedErrors(); - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 97c83a3e5..ded01f8dd 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -37,59 +37,42 @@ namespace fs = boost::filesystem; SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion): - SolidityExecutionFramework(_evmVersion) + SolidityExecutionFramework(_evmVersion), + EVMVersionRestrictedTestCase(_filename) { - ifstream file(_filename); - soltestAssert(file, "Cannot open test contract: \"" + _filename + "\"."); - file.exceptions(ios::badbit); + m_source = m_reader.source(); + m_lineOffset = m_reader.lineNumber(); - std::tie(m_source, m_lineOffset) = parseSourceAndSettingsWithLineNumbers(file); - - if (m_settings.count("compileViaYul")) + string choice = m_reader.stringSetting("compileViaYul", "false"); + if (choice == "also") { - if (m_settings["compileViaYul"] == "also") - { - m_validatedSettings["compileViaYul"] = m_settings["compileViaYul"]; - m_runWithYul = true; - m_runWithoutYul = true; - } - else - { - m_validatedSettings["compileViaYul"] = "only"; - m_runWithYul = true; - m_runWithoutYul = false; - } - m_settings.erase("compileViaYul"); + m_runWithYul = true; + m_runWithoutYul = true; } - if (m_settings.count("ABIEncoderV1Only")) + else if (choice == "true") { - if (m_settings["ABIEncoderV1Only"] == "true") - { - m_validatedSettings["ABIEncoderV1Only"] = "true"; - m_runWithABIEncoderV1Only = true; - } - m_settings.erase("ABIEncoderV1Only"); + m_runWithYul = true; + m_runWithoutYul = false; } + else if (choice == "false") + { + m_runWithYul = false; + m_runWithoutYul = true; + } + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + ".")); + m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) m_shouldRun = false; - if (m_settings.count("revertStrings")) - { - auto revertStrings = revertStringsFromString(m_settings["revertStrings"]); - if (revertStrings) - m_revertStrings = *revertStrings; - m_validatedSettings["revertStrings"] = revertStringsToString(m_revertStrings); - m_settings.erase("revertStrings"); - } + auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default")); + soltestAssert(revertStrings, "Invalid revertStrings setting."); + m_revertStrings = revertStrings.value(); - if (m_settings.count("allowNonExistingFunctions")) - { - m_validatedSettings["allowNonExistingFunctions"] = true; - m_settings.erase("allowNonExistingFunctions"); - } + m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false); - parseExpectations(file); + parseExpectations(m_reader.stream()); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); } @@ -152,7 +135,7 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref else { soltestAssert( - m_validatedSettings.count("allowNonExistingFunctions") || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), + m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), "The function " + test.call().signature + " is not known to the compiler" ); diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 0ea486cad..94c29e193 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -65,6 +65,7 @@ private: bool m_runWithYul = false; bool m_runWithoutYul = true; bool m_runWithABIEncoderV1Only = false; + bool m_allowNonExistingFunctions = false; }; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 2c3dfebac..69e645494 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -154,7 +154,6 @@ BOOST_AUTO_TEST_CASE(while_loop) ) } - BOOST_AUTO_TEST_CASE(do_while_loop) { char const* sourceCode = R"( @@ -1003,22 +1002,6 @@ BOOST_AUTO_TEST_CASE(constructor) ) } -BOOST_AUTO_TEST_CASE(balance) -{ - char const* sourceCode = R"( - contract test { - constructor() public payable {} - function getBalance() public returns (uint256 balance) { - return address(this).balance; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 23); - ABI_CHECK(callContractFunction("getBalance()"), encodeArgs(23)); - ) -} - BOOST_AUTO_TEST_CASE(blockchain) { char const* sourceCode = R"( @@ -1041,39 +1024,6 @@ BOOST_AUTO_TEST_CASE(blockchain) ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); } -BOOST_AUTO_TEST_CASE(msg_sig) -{ - char const* sourceCode = R"( - contract test { - function foo(uint256 a) public returns (bytes4 value) { - return msg.sig; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(util::keccak256("foo(uint256)")).asBytes()))); - ) -} - -BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) -{ - char const* sourceCode = R"( - contract test { - function boo() public returns (bytes4 value) { - return msg.sig; - } - function foo(uint256 a) public returns (bytes4 value) { - return boo(); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(util::keccak256("foo(uint256)")).asBytes()))); - ) -} - BOOST_AUTO_TEST_CASE(now) { char const* sourceCode = R"( @@ -1660,54 +1610,6 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_in_calls) ABI_CHECK(callContractFunction("callHelper(bytes2,bool)", string("\0a", 2), true), encodeArgs(string("\0a\0\0\0", 5))); } -BOOST_AUTO_TEST_CASE(constructor_arguments_internal) -{ - char const* sourceCode = R"( - contract Helper { - bytes3 name; - bool flag; - - constructor(bytes3 x, bool f) public { - name = x; - flag = f; - } - function getName() public returns (bytes3 ret) { return name; } - function getFlag() public returns (bool ret) { return flag; } - } - contract Main { - Helper h; - constructor() public { - h = new Helper("abc", true); - } - function getFlag() public returns (bool ret) { return h.getFlag(); } - function getName() public returns (bytes3 ret) { return h.getName(); } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - ABI_CHECK(callContractFunction("getFlag()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("getName()"), encodeArgs("abc")); -} - -BOOST_AUTO_TEST_CASE(constructor_arguments_external) -{ - char const* sourceCode = R"( - contract Main { - bytes3 name; - bool flag; - - constructor(bytes3 x, bool f) public { - name = x; - flag = f; - } - function getName() public returns (bytes3 ret) { return name; } - function getFlag() public returns (bool ret) { return flag; } - } - )"; - compileAndRun(sourceCode, 0, "Main", encodeArgs("abc", true)); - ABI_CHECK(callContractFunction("getFlag()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("getName()"), encodeArgs("abc")); -} - BOOST_AUTO_TEST_CASE(constructor_with_long_arguments) { char const* sourceCode = R"( @@ -1736,63 +1638,6 @@ BOOST_AUTO_TEST_CASE(constructor_with_long_arguments) ABI_CHECK(callContractFunction("b()"), encodeDyn(b)); } -BOOST_AUTO_TEST_CASE(constructor_static_array_argument) -{ - char const* sourceCode = R"( - contract C { - uint public a; - uint[3] public b; - - constructor(uint _a, uint[3] memory _b) public { - a = _a; - b = _b; - } - } - )"; - compileAndRun(sourceCode, 0, "C", encodeArgs(u256(1), u256(2), u256(3), u256(4))); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("b(uint256)", u256(0)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("b(uint256)", u256(1)), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("b(uint256)", u256(2)), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(constant_var_as_array_length) -{ - char const* sourceCode = R"( - contract C { - uint constant LEN = 3; - uint[LEN] public a; - - constructor(uint[LEN] memory _a) public { - a = _a; - } - } - )"; - compileAndRun(sourceCode, 0, "C", encodeArgs(u256(1), u256(2), u256(3))); - ABI_CHECK(callContractFunction("a(uint256)", u256(0)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("a(uint256)", u256(1)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("a(uint256)", u256(2)), encodeArgs(u256(3))); -} - -BOOST_AUTO_TEST_CASE(functions_called_by_constructor) -{ - char const* sourceCode = R"( - contract Test { - bytes3 name; - bool flag; - constructor() public { - setName("abc"); - } - function getName() public returns (bytes3 ret) { return name; } - function setName(bytes3 _name) private { name = _name; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); - ) -} - BOOST_AUTO_TEST_CASE(contracts_as_addresses) { char const* sourceCode = R"( @@ -1813,97 +1658,6 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5))); } -BOOST_AUTO_TEST_CASE(gas_and_value_basic) -{ - char const* sourceCode = R"( - contract helper { - bool flag; - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - function setFlag() public { flag = true; } - function getFlag() public returns (bool fl) { return flag; } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public payable returns (uint256 bal) { - return h.getBalance.value(amount)(); - } - function outOfGas() public returns (bool ret) { - h.setFlag.gas(2)(); // should fail due to OOG - return true; - } - function checkState() public returns (bool flagAfter, uint myBal) { - flagAfter = h.getFlag(); - myBal = address(this).balance; - } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5)); - // call to helper should not succeed but amount should be transferred anyway - BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes()); - BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); -} - -BOOST_AUTO_TEST_CASE(gas_and_value_brace_syntax) -{ - char const* sourceCode = R"( - contract helper { - bool flag; - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - function setFlag() public { flag = true; } - function getFlag() public returns (bool fl) { return flag; } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public payable returns (uint256 bal) { - return h.getBalance{value: amount}(); - } - function outOfGas() public returns (bool ret) { - h.setFlag{gas: 2}(); // should fail due to OOG - return true; - } - function checkState() public returns (bool flagAfter, uint myBal) { - flagAfter = h.getFlag(); - myBal = address(this).balance; - } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(5)); - // call to helper should not succeed but amount should be transferred anyway - BOOST_REQUIRE(callContractFunction("outOfGas()") == bytes()); - BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); -} - -BOOST_AUTO_TEST_CASE(gasleft_decrease) -{ - char const* sourceCode = R"( - contract C { - uint v; - function f() public returns (bool) { - uint startGas = gasleft(); - v++; - assert(startGas > gasleft()); - return true; - } - function g() public returns (bool) { - uint startGas = gasleft(); - assert(startGas > gasleft()); - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(gaslimit) { char const* sourceCode = R"( @@ -1966,221 +1720,6 @@ BOOST_AUTO_TEST_CASE(blockhash) ABI_CHECK(callContractFunction("f()"), encodeDyn(hashes)); } -BOOST_AUTO_TEST_CASE(value_complex) -{ - char const* sourceCode = R"( - contract helper { - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public payable returns (uint256 bal) { - uint someStackElement = 20; - return h.getBalance.value(amount).gas(1000).value(amount + 3)(); - } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8)); -} - -BOOST_AUTO_TEST_CASE(value_insane) -{ - char const* sourceCode = R"( - contract helper { - function getBalance() payable public returns (uint256 myBalance) { - return address(this).balance; - } - } - contract test { - helper h; - constructor() public payable { h = new helper(); } - function sendAmount(uint amount) public returns (uint256 bal) { - return h.getBalance.value(amount).gas(1000).value(amount + 3)();// overwrite value - } - } - )"; - compileAndRun(sourceCode, 20); - BOOST_REQUIRE(callContractFunction("sendAmount(uint256)", 5) == encodeArgs(8)); -} - -BOOST_AUTO_TEST_CASE(value_for_constructor) -{ - char const* sourceCode = R"( - contract Helper { - bytes3 name; - bool flag; - constructor(bytes3 x, bool f) public payable { - name = x; - flag = f; - } - function getName() public returns (bytes3 ret) { return name; } - function getFlag() public returns (bool ret) { return flag; } - } - contract Main { - Helper h; - constructor() public payable { - h = (new Helper).value(10)("abc", true); - } - function getFlag() public returns (bool ret) { return h.getFlag(); } - function getName() public returns (bytes3 ret) { return h.getName(); } - function getBalances() public returns (uint me, uint them) { me = address(this).balance; them = address(h).balance;} - } - )"; - compileAndRun(sourceCode, 22, "Main"); - BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true)); - BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); - BOOST_REQUIRE(callContractFunction("getBalances()") == encodeArgs(12, 10)); -} - -BOOST_AUTO_TEST_CASE(virtual_function_calls) -{ - char const* sourceCode = R"( - contract Base { - function f() public returns (uint i) { return g(); } - function g() public virtual returns (uint i) { return 1; } - } - contract Derived is Base { - function g() public override returns (uint i) { return 2; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(2)); - ) -} - -BOOST_AUTO_TEST_CASE(access_base_storage) -{ - char const* sourceCode = R"( - contract Base { - uint dataBase; - function getViaBase() public returns (uint i) { return dataBase; } - } - contract Derived is Base { - uint dataDerived; - function setData(uint base, uint derived) public returns (bool r) { - dataBase = base; - dataDerived = derived; - return true; - } - function getViaDerived() public returns (uint base, uint derived) { - base = dataBase; - derived = dataDerived; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("setData(uint256,uint256)", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("getViaBase()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("getViaDerived()"), encodeArgs(1, 2)); - ) -} - -BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance) -{ - char const* sourceCode = R"( - contract Base { - uint data; - function setData(uint i) public { data = i; } - function getViaBase() public returns (uint i) { return data; } - } - contract A is Base { function setViaA(uint i) public { setData(i); } } - contract B is Base { function getViaB() public returns (uint i) { return getViaBase(); } } - contract Derived is Base, B, A { } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getViaB()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("setViaA(uint256)", 23), encodeArgs()); - ABI_CHECK(callContractFunction("getViaB()"), encodeArgs(23)); - ) -} - -BOOST_AUTO_TEST_CASE(explicit_base_class) -{ - char const* sourceCode = R"( - contract BaseBase { function g() public virtual returns (uint r) { return 1; } } - contract Base is BaseBase { function g() public virtual override returns (uint r) { return 2; } } - contract Derived is Base { - function f() public returns (uint r) { return BaseBase.g(); } - function g() public override returns (uint r) { return 3; } - } - )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(base_constructor_arguments) -{ - char const* sourceCode = R"( - contract BaseBase { - uint m_a; - constructor(uint a) public { - m_a = a; - } - } - contract Base is BaseBase(7) { - constructor() public { - m_a *= m_a; - } - } - contract Derived is Base() { - function getA() public returns (uint r) { return m_a; } - } - )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(7 * 7)); -} - -BOOST_AUTO_TEST_CASE(function_usage_in_constructor_arguments) -{ - char const* sourceCode = R"( - contract BaseBase { - uint m_a; - constructor(uint a) public { - m_a = a; - } - function g() public returns (uint r) { return 2; } - } - contract Base is BaseBase(BaseBase.g()) { - } - contract Derived is Base() { - function getA() public returns (uint r) { return m_a; } - } - )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(virtual_function_usage_in_constructor_arguments) -{ - char const* sourceCode = R"( - contract BaseBase { - uint m_a; - constructor(uint a) public { - m_a = a; - } - function overridden() public virtual returns (uint r) { return 1; } - function g() public returns (uint r) { return overridden(); } - } - contract Base is BaseBase(BaseBase.g()) { - } - contract Derived is Base() { - function getA() public returns (uint r) { return m_a; } - function overridden() public override returns (uint r) { return 2; } - } - )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(2)); -} - BOOST_AUTO_TEST_CASE(internal_constructor) { char const* sourceCode = R"( @@ -2191,260 +1730,6 @@ BOOST_AUTO_TEST_CASE(internal_constructor) BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); } -BOOST_AUTO_TEST_CASE(function_modifier) -{ - char const* sourceCode = R"( - contract C { - function getOne() payable nonFree public returns (uint r) { return 1; } - modifier nonFree { if (msg.value > 0) _; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getOne()"), encodeArgs(0)); - ABI_CHECK(callContractFunctionWithValue("getOne()", 1), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_local_variables) -{ - char const* sourceCode = R"( - contract C { - modifier mod1 { uint8 a = 1; uint8 b = 2; _; } - modifier mod2(bool a) { if (a) return; else _; } - function f(bool a) mod1 mod2(a) public returns (uint r) { return 3; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(0)); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(3)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_loop) -{ - char const* sourceCode = R"( - contract C { - modifier repeat(uint count) { uint i; for (i = 0; i < count; ++i) _; } - function f() repeat(10) public returns (uint r) { r += 1; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(10)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation) -{ - char const* sourceCode = R"( - contract C { - modifier repeat(bool twice) { if (twice) _; _; } - function f(bool twice) repeat(twice) public returns (uint r) { r += 1; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return) -{ - // Note that return sets the return variable and jumps to the end of the current function or - // modifier code block. - char const* sourceCode = R"( - contract C { - modifier repeat(bool twice) { if (twice) _; _; } - function f(bool twice) repeat(twice) public returns (uint r) { r += 1; return r; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_overriding) -{ - char const* sourceCode = R"( - contract A { - function f() mod public returns (bool r) { return true; } - modifier mod virtual { _; } - } - contract C is A { - modifier mod override { if (false) _; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(false)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context) -{ - char const* sourceCode = R"( - contract A { - uint data; - constructor() mod1 public { f1(); } - function f1() mod2 public { data |= 0x1; } - function f2() public { data |= 0x20; } - function f3() public virtual { } - modifier mod1 virtual { f2(); _; } - modifier mod2 { f3(); if (false) _; } - function getData() public returns (uint r) { return data; } - } - contract C is A { - modifier mod1 override { f4(); _; } - function f3() public override { data |= 0x300; } - function f4() public { data |= 0x4000; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getData()"), encodeArgs(0x4300)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_for_constructor) -{ - char const* sourceCode = R"( - contract A { - uint data; - constructor() mod1 public { data |= 2; } - modifier mod1 virtual { data |= 1; _; } - function getData() public returns (uint r) { return data; } - } - contract C is A { - modifier mod1 override { data |= 4; _; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getData()"), encodeArgs(4 | 2)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_multiple_times) -{ - char const* sourceCode = R"( - contract C { - uint public a; - modifier mod(uint x) { a += x; _; } - function f(uint x) mod(2) mod(5) mod(x) public returns(uint) { return a; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(2 + 5 + 3)); - ABI_CHECK(callContractFunction("a()"), encodeArgs(2 + 5 + 3)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars) -{ - char const* sourceCode = R"( - contract C { - uint public a; - modifier mod(uint x) { uint b = x; a += b; _; a -= b; assert(b == x); } - function f(uint x) mod(2) mod(5) mod(x) public returns(uint) { return a; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(2 + 5 + 3)); - ABI_CHECK(callContractFunction("a()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_library) -{ - char const* sourceCode = R"( - library L { - struct S { uint v; } - modifier mod(S storage s) { s.v++; _; } - function libFun(S storage s) mod(s) internal { s.v += 0x100; } - } - - contract Test { - using L for *; - L.S s; - - function f() public returns (uint) { - s.libFun(); - L.libFun(s); - return s.v; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); -} - -BOOST_AUTO_TEST_CASE(function_modifier_library_inheritance) -{ - // Tests that virtual lookup for modifiers in libraries does not consider - // the current inheritance hierarchy. - - char const* sourceCode = R"( - library L { - struct S { uint v; } - modifier mod(S storage s) { s.v++; _; } - function libFun(S storage s) mod(s) internal { s.v += 0x100; } - } - - contract Test { - using L for *; - L.S s; - modifier mod(L.S storage) { revert(); _; } - - function f() public returns (uint) { - s.libFun(); - L.libFun(s); - return s.v; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); -} - -BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint r) { - uint; uint; uint; uint; - int x = -7; - return uint(x); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(-7))); - ) -} - -BOOST_AUTO_TEST_CASE(super) -{ - char const* sourceCode = R"( - contract A { function f() public virtual returns (uint r) { return 1; } } - contract B is A { function f() public virtual override returns (uint r) { return super.f() | 2; } } - contract C is A { function f() public virtual override returns (uint r) { return super.f() | 4; } } - contract D is B, C { function f() public override(B, C) returns (uint r) { return super.f() | 8; } } - )"; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1 | 2 | 4 | 8)); -} - -BOOST_AUTO_TEST_CASE(super_in_constructor) -{ - char const* sourceCode = R"( - contract A { function f() public virtual returns (uint r) { return 1; } } - contract B is A { function f() public virtual override returns (uint r) { return super.f() | 2; } } - contract C is A { function f() public virtual override returns (uint r) { return super.f() | 4; } } - contract D is B, C { uint data; constructor() public { data = super.f() | 8; } function f() public override (B, C) returns (uint r) { return data; } } - )"; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1 | 2 | 4 | 8)); -} - -BOOST_AUTO_TEST_CASE(super_alone) -{ - char const* sourceCode = R"( - contract A { function f() public { super; } } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "A"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ) -} - BOOST_AUTO_TEST_CASE(default_fallback_throws) { char const* sourceCode = R"YY( @@ -3085,170 +2370,6 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) ) } -BOOST_AUTO_TEST_CASE(empty_name_return_parameter) -{ - char const* sourceCode = R"( - contract test { - function f(uint k) public returns(uint){ - return k; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", 9), encodeArgs(9)); - ) -} - -BOOST_AUTO_TEST_CASE(sha256_empty) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (bytes32) { - return sha256(""); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); -} - -BOOST_AUTO_TEST_CASE(ripemd160_empty) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (bytes20) { - return ripemd160(""); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), fromHex("0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000")); -} - -BOOST_AUTO_TEST_CASE(keccak256_empty) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (bytes32) { - return keccak256(""); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ) -} - -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) -{ - char const* sourceCode = R"( - contract c { - function foo(uint a, uint b, uint c) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, c)); - } - } - )"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - toBigEndian(u256(12)) + - toBigEndian(u256(13)) - ) - )); -} - -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_numeric_literals) -{ - char const* sourceCode = R"( - contract c { - function foo(uint a, uint16 b) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, uint8(145))); - } - } - )"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - bytes{0x0, 0xc} + - bytes(1, 0x91) - ) - )); -} - -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_string_literals) -{ - char const* sourceCode = R"( - contract c { - function foo() public returns (bytes32 d) - { - d = keccak256("foo"); - } - function bar(uint a, uint16 b) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); - } - } - )"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo()"), encodeArgs(util::keccak256("foo"))); - - ABI_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - bytes{0x0, 0xc} + - bytes(1, 0x91) + - bytes{0x66, 0x6f, 0x6f} - ) - )); -} - -BOOST_AUTO_TEST_CASE(keccak256_with_bytes) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function foo() public returns (bool) - { - data.push("f"); - data.push("o"); - data.push("o"); - return keccak256(data) == keccak256("foo"); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(iterated_keccak256_with_bytes) -{ - char const* sourceCode = R"ABC( - contract c { - bytes data; - function foo() public returns (bytes32) - { - data.push("x"); - data.push("y"); - data.push("z"); - return keccak256(abi.encodePacked("b", keccak256(data), "a")); - } - } - )ABC"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo()"), encodeArgs( - u256(util::keccak256(bytes{'b'} + util::keccak256("xyz").asBytes() + bytes{'a'})) - )); -} - BOOST_AUTO_TEST_CASE(generic_call) { char const* sourceCode = R"**( @@ -3414,7 +2535,6 @@ BOOST_AUTO_TEST_CASE(library_call_protection) ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); } - BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) { char const* sourceCode = R"( @@ -3439,25 +2559,6 @@ BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); } -BOOST_AUTO_TEST_CASE(store_bytes) -{ - // this test just checks that the copy loop does not mess up the stack - char const* sourceCode = R"( - contract C { - function save() public returns (uint r) { - r = 23; - savedData = msg.data; - r = 24; - } - bytes savedData; - } - )"; - compileAndRun(sourceCode); - // empty copy loop - ABI_CHECK(callContractFunction("save()"), encodeArgs(24)); - ABI_CHECK(callContractFunction("save()", "abcdefg"), encodeArgs(24)); -} - BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) { char const* sourceCode = R"( @@ -3655,61 +2756,6 @@ BOOST_AUTO_TEST_CASE(bytes_inside_mappings) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(bytes_length_member) -{ - char const* sourceCode = R"( - contract c { - function set() public returns (bool) { data = msg.data; return true; } - function getLength() public returns (uint) { return data.length; } - bytes data; - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getLength()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("set()", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("getLength()"), encodeArgs(4+32+32)); -} - -BOOST_AUTO_TEST_CASE(struct_copy) -{ - char const* sourceCode = R"( - contract c { - struct Nested { uint x; uint y; } - struct Struct { uint a; mapping(uint => Struct) b; Nested nested; uint c; } - mapping(uint => Struct) data; - function set(uint k) public returns (bool) { - data[k].a = 1; - data[k].nested.x = 3; - data[k].nested.y = 4; - data[k].c = 2; - return true; - } - function copy(uint from, uint to) public returns (bool) { - data[to] = data[from]; - return true; - } - function retrieve(uint k) public returns (uint a, uint x, uint y, uint c) - { - a = data[k].a; - x = data[k].nested.x; - y = data[k].nested.y; - c = data[k].c; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set(uint256)", 7), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 7), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 7), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 8), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 0, 7), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 7), encodeArgs(0, 0, 0, 0)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 8), encodeArgs(1, 3, 4, 2)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 7, 8), encodeArgs(true)); - ABI_CHECK(callContractFunction("retrieve(uint256)", 8), encodeArgs(0, 0, 0, 0)); -} - BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) { char const* sourceCode = R"( @@ -3746,84 +2792,6 @@ BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(struct_copy_via_local) -{ - char const* sourceCode = R"( - contract c { - struct Struct { uint a; uint b; } - Struct data1; - Struct data2; - function test() public returns (bool) { - data1.a = 1; - data1.b = 2; - Struct memory x = data1; - data2 = x; - return data2.a == data1.a && data2.b == data1.b; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(using_enums) -{ - char const* sourceCode = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - constructor() public - { - choices = ActionChoices.GoStraight; - } - function getChoice() public returns (uint d) - { - d = uint256(choices); - } - ActionChoices choices; - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getChoice()"), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(enum_explicit_overflow) -{ - char const* sourceCode = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight } - constructor() public - { - } - function getChoiceExp(uint x) public returns (uint d) - { - choice = ActionChoices(x); - d = uint256(choice); - } - function getChoiceFromSigned(int x) public returns (uint d) - { - choice = ActionChoices(x); - d = uint256(choice); - } - function getChoiceFromNegativeLiteral() public returns (uint d) - { - choice = ActionChoices(-1); - d = uint256(choice); - } - ActionChoices choice; - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - // These should throw - ABI_CHECK(callContractFunction("getChoiceExp(uint256)", 3), encodeArgs()); - ABI_CHECK(callContractFunction("getChoiceFromSigned(int256)", -1), encodeArgs()); - ABI_CHECK(callContractFunction("getChoiceFromNegativeLiteral()"), encodeArgs()); - // These should work - ABI_CHECK(callContractFunction("getChoiceExp(uint256)", 2), encodeArgs(2)); - ABI_CHECK(callContractFunction("getChoiceExp(uint256)", 0), encodeArgs(0)); - ) -} - BOOST_AUTO_TEST_CASE(storing_invalid_boolean) { char const* sourceCode = R"( @@ -3867,75 +2835,6 @@ BOOST_AUTO_TEST_CASE(storing_invalid_boolean) BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Ev(bool)"))); } - -BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name) -{ - char const* sourceCode = R"( - contract test { - enum Choice { A, B, C } - function answer () public returns (test.Choice _ret) - { - _ret = test.Choice.B; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("answer()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(using_inherited_enum) -{ - char const* sourceCode = R"( - contract base { - enum Choice { A, B, C } - } - - contract test is base { - function answer () public returns (Choice _ret) - { - _ret = Choice.B; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("answer()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(using_inherited_enum_excplicitly) -{ - char const* sourceCode = R"( - contract base { - enum Choice { A, B, C } - } - - contract test is base { - function answer () public returns (base.Choice _ret) - { - _ret = base.Choice.B; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("answer()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) -{ - char const* sourceCode = R"( - contract c { - enum Truth { False, True } - function test() public returns (uint) - { - return uint(Truth(uint8(0x701))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); - ) -} - BOOST_AUTO_TEST_CASE(struct_referencing) { static char const* sourceCode = R"( @@ -4042,80 +2941,6 @@ BOOST_AUTO_TEST_CASE(enum_referencing) ABI_CHECK(callContractFunction("y()"), encodeArgs(3)); } -BOOST_AUTO_TEST_CASE(inline_member_init) -{ - char const* sourceCode = R"( - contract test { - constructor() public { - m_b = 6; - m_c = 8; - } - uint m_a = 5; - uint m_b; - uint m_c = 7; - function get() public returns (uint a, uint b, uint c){ - a = m_a; - b = m_b; - c = m_c; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get()"), encodeArgs(5, 6, 8)); -} -BOOST_AUTO_TEST_CASE(inline_member_init_inheritence) -{ - char const* sourceCode = R"( - contract Base { - constructor() public {} - uint m_base = 5; - function getBMember() public returns (uint i) { return m_base; } - } - contract Derived is Base { - constructor() public {} - uint m_derived = 6; - function getDMember() public returns (uint i) { return m_derived; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getBMember()"), encodeArgs(5)); - ABI_CHECK(callContractFunction("getDMember()"), encodeArgs(6)); -} - -BOOST_AUTO_TEST_CASE(inline_member_init_inheritence_without_constructor) -{ - char const* sourceCode = R"( - contract Base { - uint m_base = 5; - function getBMember() public returns (uint i) { return m_base; } - } - contract Derived is Base { - uint m_derived = 6; - function getDMember() public returns (uint i) { return m_derived; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getBMember()"), encodeArgs(5)); - ABI_CHECK(callContractFunction("getDMember()"), encodeArgs(6)); -} - -BOOST_AUTO_TEST_CASE(external_function) -{ - char const* sourceCode = R"( - contract c { - function f(uint a) public returns (uint) { return a; } - function test(uint a, uint b) external returns (uint r_a, uint r_b) { - r_a = f(a + 7); - r_b = b; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test(uint256,uint256)", 2, 3), encodeArgs(2+7, 3)); - ) -} - BOOST_AUTO_TEST_CASE(bytes_in_arguments) { char const* sourceCode = R"( @@ -4147,120 +2972,6 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) ); } -BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage) -{ - char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[2**10] data; - uint[2**10 + 3] ids; - function setIDStatic(uint id) public { ids[2] = id; } - function setID(uint index, uint id) public { ids[index] = id; } - function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } - function getID(uint index) public returns (uint) { return ids[index]; } - function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("setIDStatic(uint256)", 11), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 2), encodeArgs(11)); - ABI_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 7), encodeArgs(8)); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9), bytes()); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11), bytes()); - ABI_CHECK(callContractFunction("getData(uint256)", 7), encodeArgs(8, 9)); - ABI_CHECK(callContractFunction("getData(uint256)", 8), encodeArgs(10, 11)); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(u256(1) << 10, (u256(1) << 10) + 3)); -} - -BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage) -{ - char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[] data; - uint[] ids; - function setIDStatic(uint id) public { ids[2] = id; } - function setID(uint index, uint id) public { ids[index] = id; } - function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } - function getID(uint index) public returns (uint) { return ids[index]; } - function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } - function setLengths(uint l1, uint l2) public { - while (data.length < l1) - data.push(); - while (ids.length < l2) - ids.push(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(0, 0)); - ABI_CHECK(callContractFunction("setLengths(uint256,uint256)", 48, 49), bytes()); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(48, 49)); - ABI_CHECK(callContractFunction("setIDStatic(uint256)", 11), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 2), encodeArgs(11)); - ABI_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 7), encodeArgs(8)); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9), bytes()); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11), bytes()); - ABI_CHECK(callContractFunction("getData(uint256)", 7), encodeArgs(8, 9)); - ABI_CHECK(callContractFunction("getData(uint256)", 8), encodeArgs(10, 11)); -} - -BOOST_AUTO_TEST_CASE(fixed_out_of_bounds_array_access) -{ - char const* sourceCode = R"( - contract c { - uint[4] data; - function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } - function get(uint index) public returns (uint) { return data[index]; } - function length() public returns (uint) { return data.length; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 3, 4), encodeArgs(true)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 5), bytes()); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 400, 5), bytes()); - ABI_CHECK(callContractFunction("get(uint256)", 3), encodeArgs(4)); - ABI_CHECK(callContractFunction("get(uint256)", 4), bytes()); - ABI_CHECK(callContractFunction("get(uint256)", 400), bytes()); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ) -} - -BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function enlarge(uint amount) public returns (uint) { - while (data.length < amount) - data.push(); - return data.length; - } - function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } - function get(uint index) public returns (uint) { return data[index]; } - function length() public returns (uint) { return data.length; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("length()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("get(uint256)", 3), bytes()); - ABI_CHECK(callContractFunction("enlarge(uint256)", 4), encodeArgs(4)); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 3, 4), encodeArgs(true)); - ABI_CHECK(callContractFunction("get(uint256)", 3), encodeArgs(4)); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("set(uint256,uint256)", 4, 8), bytes()); - ABI_CHECK(callContractFunction("length()"), encodeArgs(4)); - ); -} - BOOST_AUTO_TEST_CASE(fixed_array_cleanup) { char const* sourceCode = R"( @@ -4390,103 +3101,6 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_static) -{ - char const* sourceCode = R"( - contract c { - uint[40] data1; - uint[20] data2; - function test() public returns (uint x, uint y){ - data1[30] = 4; - data1[2] = 7; - data1[3] = 9; - data2[3] = 8; - data1 = data2; - x = data1[3]; - y = data1[30]; // should be cleared - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(8, 0)); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_dynamic) -{ - char const* sourceCode = R"( - contract c { - uint[9] data1; - uint[] data2; - function test() public returns (uint x, uint y){ - data1[8] = 4; - data2 = data1; - x = data2.length; - y = data2[8]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(9, 4)); -} - -BOOST_AUTO_TEST_CASE(array_copy_different_packing) -{ - char const* sourceCode = R"( - contract c { - bytes8[] data1; // 4 per slot - bytes10[] data2; // 3 per slot - function test() public returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) { - data1 = new bytes8[](9); - for (uint i = 0; i < data1.length; ++i) - data1[i] = bytes8(uint64(i)); - data2 = data1; - a = data2[1]; - b = data2[2]; - c = data2[3]; - d = data2[4]; - e = data2[5]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - asString(fromHex("0000000000000001")), - asString(fromHex("0000000000000002")), - asString(fromHex("0000000000000003")), - asString(fromHex("0000000000000004")), - asString(fromHex("0000000000000005")) - )); -} - -BOOST_AUTO_TEST_CASE(array_copy_target_simple) -{ - char const* sourceCode = R"( - contract c { - bytes8[9] data1; // 4 per slot - bytes17[10] data2; // 1 per slot, no offset counter - function test() public returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) { - for (uint i = 0; i < data1.length; ++i) - data1[i] = bytes8(uint64(i)); - data2[8] = data2[9] = bytes8(uint64(2)); - data2 = data1; - a = data2[1]; - b = data2[2]; - c = data2[3]; - d = data2[4]; - e = data2[9]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - asString(fromHex("0000000000000001")), - asString(fromHex("0000000000000002")), - asString(fromHex("0000000000000003")), - asString(fromHex("0000000000000004")), - asString(fromHex("0000000000000000")) - )); -} - BOOST_AUTO_TEST_CASE(array_copy_target_leftover) { // test that leftover elements in the last slot of target are correctly cleared during assignment @@ -4510,41 +3124,7 @@ BOOST_AUTO_TEST_CASE(array_copy_target_leftover) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - u256("0xffffffff"), - asString(fromHex("0000000000000000""000000000a000900""0800070006000500""0400030002000100")), - asString(fromHex("0000000000000000""0000000000000000""0000000000000000""0000000000000000")) - )); -} - -BOOST_AUTO_TEST_CASE(array_copy_target_leftover2) -{ - // since the copy always copies whole slots, we have to make sure that the source size maxes - // out a whole slot and at the same time there are still elements left in the target at that point - char const* sourceCode = R"( - contract c { - bytes8[4] data1; // fits into one slot - bytes10[6] data2; // 4 elements need two slots - function test() public returns (bytes10 r1, bytes10 r2, bytes10 r3) { - data1[0] = bytes8(uint64(1)); - data1[1] = bytes8(uint64(2)); - data1[2] = bytes8(uint64(3)); - data1[3] = bytes8(uint64(4)); - for (uint i = 0; i < data2.length; ++i) - data2[i] = bytes10(uint80(0xffff00 | (1 + i))); - data2 = data1; - r1 = data2[3]; - r2 = data2[4]; - r3 = data2[5]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - asString(fromHex("0000000000000004")), - asString(fromHex("0000000000000000")), - asString(fromHex("0000000000000000")) - )); + ABI_CHECK(callContractFunction("test()"), encodeArgs(u256("0xffffffff"), asString(fromHex("0000000000000000000000000a00090008000700060005000400030002000100")), asString(fromHex("0000000000000000000000000000000000000000000000000000000000000000")))); } BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) @@ -4625,167 +3205,6 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) ); } -BOOST_AUTO_TEST_CASE(array_copy_storage_abi_signed) -{ - // NOTE: This does not really test copying from storage to ABI directly, - // because it will always copy to memory first. - char const* sourceCode = R"( - contract c { - int16[] x; - function test() public returns (int16[] memory) { - x.push(int16(-1)); - x.push(int16(-1)); - x.push(int16(8)); - x.push(int16(-16)); - x.push(int16(-2)); - x.push(int16(6)); - x.push(int16(8)); - x.push(int16(-1)); - return x; - } - } - )"; - compileAndRun(sourceCode); - bytes valueSequence; - ABI_CHECK(callContractFunction("test()"), encodeArgs(0x20, 8, - u256(-1), - u256(-1), - u256(8), - u256(-16), - u256(-2), - u256(6), - u256(8), - u256(-1) - )); -} - -BOOST_AUTO_TEST_CASE(array_push) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public returns (uint x, uint y, uint z, uint l) { - data.push(5); - x = data[0]; - data.push(4); - y = data[1]; - data.push(3); - l = data.length; - z = data[2]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(5, 4, 3, 3)); -} - -BOOST_AUTO_TEST_CASE(array_push_struct) -{ - char const* sourceCode = R"( - contract c { - struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; } - S[] data; - function test() public returns (uint16, uint16, uint16, uint16) { - S memory s; - s.a = 2; - s.b = 3; - s.c[2] = 4; - s.d = new uint16[](4); - s.d[2] = 5; - data.push(s); - return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 3, 4, 5)); -} - -BOOST_AUTO_TEST_CASE(array_push_packed_array) -{ - char const* sourceCode = R"( - contract c { - uint80[] x; - function test() public returns (uint80, uint80, uint80, uint80) { - x.push(1); - x.push(2); - x.push(3); - x.push(4); - x.push(5); - x.pop(); - return (x[0], x[1], x[2], x[3]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3, 4)); -} - -BOOST_AUTO_TEST_CASE(byte_array_push) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (bool x) { - data.push(0x05); - if (data.length != 1) return true; - if (data[0] != 0x05) return true; - data.push(0x04); - if (data[1] != 0x04) return true; - data.push(0x03); - uint l = data.length; - if (data[2] != 0x03) return true; - if (l != 0x03) return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); -} - -BOOST_AUTO_TEST_CASE(byte_array_push_transition) -{ - // Tests transition between short and long encoding - char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (uint) { - for (uint8 i = 1; i < 40; i++) - { - data.push(byte(i)); - if (data.length != i) return 0x1000 + i; - if (data[data.length - 1] != byte(i)) return i; - } - for (uint8 i = 1; i < 40; i++) - if (data[i - 1] != byte(i)) return 0x1000000 + i; - return 0; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(array_pop) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public returns (uint x, uint l) { - data.push(7); - data.push(3); - x = data.length; - data.pop(); - x = data.length; - data.pop(); - l = data.length; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 0)); -} - BOOST_AUTO_TEST_CASE(array_pop_uint16_transition) { char const* sourceCode = R"( @@ -4872,21 +3291,6 @@ BOOST_AUTO_TEST_CASE(array_pop_array_transition) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(array_pop_empty_exception) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public returns (bool) { - data.pop(); - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); -} - BOOST_AUTO_TEST_CASE(array_pop_storage_empty) { char const* sourceCode = R"( @@ -4903,45 +3307,6 @@ BOOST_AUTO_TEST_CASE(array_pop_storage_empty) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(byte_array_pop) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (uint x, uint y, uint l) { - data.push(0x07); - data.push(0x03); - x = data.length; - data.pop(); - data.pop(); - data.push(0x02); - y = data.length; - l = data.length; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 1, 1)); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_empty_exception) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - bytes data; - function test() public returns (bool) { - data.pop(); - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); -} - BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) { char const* sourceCode = R"( @@ -5011,84 +3376,6 @@ BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(byte_array_pop_masking_long) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (bytes memory) { - for (uint i = 0; i < 34; i++) - data.push(0x03); - data.pop(); - return data; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - u256(0x20), - u256(33), - asString(fromHex("0303030303030303030303030303030303030303030303030303030303030303")), - asString(fromHex("03")) - )); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_copy_long) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test() public returns (bytes memory) { - for (uint i = 0; i < 33; i++) - data.push(0x03); - for (uint j = 0; j < 4; j++) - data.pop(); - return data; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs( - u256(0x20), - u256(29), - asString(fromHex("0303030303030303030303030303030303030303030303030303030303")) - )); -} - -BOOST_AUTO_TEST_CASE(array_pop_isolated) -{ - char const* sourceCode = R"( - // This tests that the compiler knows the correct size of the function on the stack. - contract c { - uint[] data; - function test() public returns (uint x) { - x = 2; - data.pop; - x = 3; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(3)); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_isolated) -{ - char const* sourceCode = R"( - // This tests that the compiler knows the correct size of the function on the stack. - contract c { - bytes data; - function test() public returns (uint x) { - x = 2; - data.pop; - x = 3; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(3)); -} - BOOST_AUTO_TEST_CASE(external_array_args) { char const* sourceCode = R"( @@ -5149,26 +3436,6 @@ BOOST_AUTO_TEST_CASE(bytes_index_access) ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193)); } -BOOST_AUTO_TEST_CASE(bytes_delete_element) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test1() external returns (bool) { - data = new bytes(100); - for (uint i = 0; i < data.length; i++) - data[i] = byte(uint8(i)); - delete data[94]; - delete data[96]; - delete data[98]; - return data[94] == 0 && uint8(data[95]) == 95 && data[96] == 0 && uint8(data[97]) == 97; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test1()"), encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) { char const* sourceCode = R"( @@ -5194,42 +3461,8 @@ BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs( - 21, 22, 23, 24, 25, 26, 27, 28, 29, // a - u256(32 * (9 + 1)), - 4, // size of b - 1, 2, 3, // b[0] - 11, 12, 13, // b[1] - 21, 22, 23, // b[2] - 31, 32, 33 // b[3] - )), encodeArgs(32)); - ABI_CHECK(callContractFunction("retrieve()"), encodeArgs( - 9, 28, 9, 28, - 4, 3, 32)); -} - -BOOST_AUTO_TEST_CASE(array_copy_nested_array) -{ - char const* sourceCode = R"( - contract c { - uint[4][] a; - uint[10][] b; - uint[][] c; - function test(uint[2][] calldata d) external returns (uint) { - a = d; - b = a; - c = b; - return c[1][1] | c[1][2] | c[1][3] | c[1][4]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test(uint256[2][])", encodeArgs( - 32, 3, - 7, 8, - 9, 10, - 11, 12 - )), encodeArgs(10)); + ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs(21, 22, 23, 24, 25, 26, 27, 28, 29, u256(32 * (9 + 1)), 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 )), encodeArgs(32)); + ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32)); } BOOST_AUTO_TEST_CASE(array_copy_including_mapping) @@ -5279,158 +3512,6 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(swap_in_storage_overwrite) -{ - // This tests a swap in storage which does not work as one - // might expect because we do not have temporary storage. - // (x, y) = (y, x) is the same as - // y = x; - // x = y; - char const* sourceCode = R"( - contract c { - struct S { uint a; uint b; } - S public x; - S public y; - function set() public { - x.a = 1; x.b = 2; - y.a = 3; y.b = 4; - } - function swap() public { - (x, y) = (y, x); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0), u256(0))); - ABI_CHECK(callContractFunction("y()"), encodeArgs(u256(0), u256(0))); - ABI_CHECK(callContractFunction("set()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1), u256(2))); - ABI_CHECK(callContractFunction("y()"), encodeArgs(u256(3), u256(4))); - ABI_CHECK(callContractFunction("swap()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1), u256(2))); - ABI_CHECK(callContractFunction("y()"), encodeArgs(u256(1), u256(2))); -} - -BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base) -{ - char const* sourceCode = R"( - contract Base { - constructor(uint i) public - { - m_i = i; - } - uint public m_i; - } - contract Derived is Base { - constructor(uint i) Base(i) public - {} - } - contract Final is Derived(4) { - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); -} - -BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base) -{ - char const* sourceCode = R"( - contract Base { - constructor(uint j) public - { - m_i = j; - } - uint public m_i; - } - contract Base1 is Base { - constructor(uint k) Base(k) public {} - } - contract Derived is Base, Base1 { - constructor(uint i) Base1(i) public - {} - } - contract Final is Derived(4) { - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); -} - -BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap) -{ - char const* sourceCode = R"( - contract Base { - constructor(uint i) public - { - m_i = i; - } - uint public m_i; - } - abstract contract Base1 is Base { - constructor(uint k) public {} - } - contract Derived is Base, Base1 { - constructor(uint i) Base(i) Base1(7) public {} - } - contract Final is Derived(4) { - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); -} - -BOOST_AUTO_TEST_CASE(simple_constant_variables_test) -{ - char const* sourceCode = R"( - contract Foo { - function getX() public returns (uint r) { return x; } - uint constant x = 56; - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("getX()"), encodeArgs(56)); -} - -BOOST_AUTO_TEST_CASE(constant_variables) -{ - char const* sourceCode = R"( - contract Foo { - uint constant x = 56; - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - ActionChoices constant choices = ActionChoices.GoLeft; - bytes32 constant st = "abc\x00\xff__"; - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ) -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_expression) -{ - char const* sourceCode = R"( - contract C { - uint constant x = 0x123 + 0x456; - function f() public returns (uint) { return x + 1; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x123 + 0x456 + 1)); -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) -{ - char const* sourceCode = R"( - contract C { - bytes32 constant x = keccak256("abc"); - function f() public returns (bytes32) { return x; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(util::keccak256("abc"))); -} - -// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented //BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) //{ // char const* sourceCode = R"( @@ -5458,98 +3539,6 @@ BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) // ABI_CHECK(callContractFunction("f()"), encodeArgs(5)); //} -BOOST_AUTO_TEST_CASE(packed_storage_structs_uint) -{ - char const* sourceCode = R"( - contract C { - struct str { uint8 a; uint16 b; uint248 c; } - str data; - function test() public returns (uint) { - data.a = 2; - if (data.a != 2) return 2; - data.b = 0xabcd; - if (data.b != 0xabcd) return 3; - data.c = 0x1234567890; - if (data.c != 0x1234567890) return 4; - if (data.a != 2) return 5; - data.a = 8; - if (data.a != 8) return 6; - if (data.b != 0xabcd) return 7; - data.b = 0xdcab; - if (data.b != 0xdcab) return 8; - if (data.c != 0x1234567890) return 9; - data.c = 0x9876543210; - if (data.c != 0x9876543210) return 10; - return 1; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(packed_storage_structs_enum) -{ - char const* sourceCode = R"( - contract C { - enum small { A, B, C, D } - enum larger { A, B, C, D, E} - struct str { small a; small b; larger c; larger d; } - str data; - function test() public returns (uint) { - data.a = small.B; - if (data.a != small.B) return 2; - data.b = small.C; - if (data.b != small.C) return 3; - data.c = larger.D; - if (data.c != larger.D) return 4; - if (data.a != small.B) return 5; - data.a = small.C; - if (data.a != small.C) return 6; - if (data.b != small.C) return 7; - data.b = small.D; - if (data.b != small.D) return 8; - if (data.c != larger.D) return 9; - data.c = larger.B; - if (data.c != larger.B) return 10; - return 1; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(packed_storage_structs_bytes) -{ - char const* sourceCode = R"( - contract C { - struct s1 { byte a; byte b; bytes10 c; bytes9 d; bytes10 e; } - struct s2 { byte a; s1 inner; byte b; byte c; } - byte x; - s2 data; - byte y; - function test() public returns (bool) { - x = 0x01; - data.a = 0x02; - data.inner.a = 0x03; - data.inner.b = 0x04; - data.inner.c = "1234567890"; - data.inner.d = "123456789"; - data.inner.e = "abcdefghij"; - data.b = 0x05; - data.c = byte(0x06); - y = 0x07; - return x == 0x01 && data.a == 0x02 && data.inner.a == 0x03 && data.inner.b == 0x04 && - data.inner.c == "1234567890" && data.inner.d == "123456789" && - data.inner.e == "abcdefghij" && data.b == 0x05 && data.c == byte(0x06) && y == 0x07; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) { char const* sourceCode = R"( @@ -5581,222 +3570,6 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) -{ - char const* sourceCode = R"( - contract test { - function f(uint k) public returns(uint d) { return k; } - function f(uint a, uint b) public returns(uint d) { return a + b; } - function g() public returns(uint d) { return f(3); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - ) -} - -BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_second) -{ - char const* sourceCode = R"( - contract test { - function f(uint a, uint b) public returns(uint d) { return a + b; } - function f(uint k) public returns(uint d) { return k; } - function g() public returns(uint d) { return f(3, 7); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(10)); - ) -} - -BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) -{ - char const* sourceCode = R"( - contract test { - function f(uint a, uint b) public returns(uint d) { return a + b; } - function f(uint k) public returns(uint d) { return k; } - function g(bool flag) public returns(uint d) { - if (flag) - return f(3); - else - return f(3, 7); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(3)); - ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs(10)); - ) -} - -BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) -{ - char const* sourceCode = R"( - contract B { function f() public returns(uint) { return 10; } } - contract C is B { - function f(uint i) public returns(uint) { return 2 * i; } - function g() public returns(uint) { return f(1); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ) -} - -BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) -{ - char const* sourceCode = R"( - contract A { function f(uint a) public returns(uint) { return 2 * a; } } - contract B { function f() public returns(uint) { return 10; } } - contract C is A, B { - function g() public returns(uint) { return f(); } - function h() public returns(uint) { return f(1); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(10)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); - ) -} - -BOOST_AUTO_TEST_CASE(super_overload) -{ - char const* sourceCode = R"( - contract A { function f(uint a) public returns(uint) { return 2 * a; } } - contract B { function f(bool b) public returns(uint) { return 10; } } - contract C is A, B { - function g() public returns(uint) { return super.f(true); } - function h() public returns(uint) { return super.f(1); } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(10)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution) -{ - char const* sourceCode = R"( - contract C { - function gasleft() public returns(uint256) { return 0; } - function f() public returns(uint256) { return gasleft(); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); - ) -} - -BOOST_AUTO_TEST_CASE(bool_conversion) -{ - char const* sourceCode = R"( - contract C { - function f(bool _b) public returns(uint) { - if (_b) - return 1; - else - return 0; - } - function g(bool _in) public returns (bool _out) { - _out = _in; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(bool)", 0), encodeArgs(0)); - ABI_CHECK(callContractFunction("f(bool)", 1), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 3), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 255), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 0), encodeArgs(0)); - ABI_CHECK(callContractFunction("g(bool)", 1), encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 3), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 255), v2 ? encodeArgs() : encodeArgs(1)); -} - -BOOST_AUTO_TEST_CASE(packed_storage_signed) -{ - char const* sourceCode = R"( - contract C { - int8 a; - uint8 b; - int8 c; - uint8 d; - function test() public returns (uint x1, uint x2, uint x3, uint x4) { - a = -2; - b = -uint8(a) * 2; - c = a * int8(120) * int8(121); - x1 = uint(a); - x2 = b; - x3 = uint(c); - x4 = d; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(-2), u256(4), u256(-112), u256(0))); -} - -BOOST_AUTO_TEST_CASE(external_types_in_calls) -{ - char const* sourceCode = R"( - contract C1 { C1 public bla; constructor(C1 x) public { bla = x; } } - contract C { - function test() public returns (C1 x, C1 y) { - C1 c = new C1(C1(9)); - x = c.bla(); - y = this.t1(C1(7)); - } - function t1(C1 a) public returns (C1) { return a; } - function t2() public returns (C1) { return C1(9); } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(9), u256(7))); - ABI_CHECK(callContractFunction("t2()"), encodeArgs(u256(9))); -} - -BOOST_AUTO_TEST_CASE(invalid_enum_compared) -{ - char const* sourceCode = R"( - contract C { - enum X { A, B } - - function test_eq() public returns (bool) { - X garbled; - assembly { - garbled := 5 - } - return garbled == garbled; - } - function test_eq_ok() public returns (bool) { - X garbled = X.A; - return garbled == garbled; - } - function test_neq() public returns (bool) { - X garbled; - assembly { - garbled := 5 - } - return garbled != garbled; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test_eq_ok()"), encodeArgs(u256(1))); - // both should throw - ABI_CHECK(callContractFunction("test_eq()"), encodeArgs()); - ABI_CHECK(callContractFunction("test_neq()"), encodeArgs()); -} - BOOST_AUTO_TEST_CASE(invalid_enum_logged) { char const* sourceCode = R"( @@ -5831,230 +3604,6 @@ BOOST_AUTO_TEST_CASE(invalid_enum_logged) ABI_CHECK(callContractFunction("test_log()"), encodeArgs()); } -BOOST_AUTO_TEST_CASE(invalid_enum_stored) -{ - char const* sourceCode = R"( - contract C { - enum X { A, B } - X public x; - - function test_store() public returns (uint) { - X garbled = X.A; - assembly { - garbled := 5 - } - x = garbled; - return 1; - } - function test_store_ok() public returns (uint) { - x = X.A; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test_store_ok()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - - // should throw - ABI_CHECK(callContractFunction("test_store()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(invalid_enum_as_external_ret) -{ - char const* sourceCode = R"( - contract C { - enum X { A, B } - - function test_return() public returns (X) { - X garbled; - assembly { - garbled := 5 - } - return garbled; - } - function test_inline_assignment() public returns (X _ret) { - assembly { - _ret := 5 - } - } - function test_assignment() public returns (X _ret) { - X tmp; - assembly { - tmp := 5 - } - _ret = tmp; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - // both should throw - ABI_CHECK(callContractFunction("test_return()"), encodeArgs()); - ABI_CHECK(callContractFunction("test_inline_assignment()"), encodeArgs()); - ABI_CHECK(callContractFunction("test_assignment()"), encodeArgs()); - ) -} - -BOOST_AUTO_TEST_CASE(invalid_enum_as_external_arg) -{ - char const* sourceCode = R"( - contract C { - enum X { A, B } - - function tested (X x) public returns (uint) { - return 1; - } - - function test() public returns (uint) { - X garbled; - - assembly { - garbled := 5 - } - - return this.tested(garbled); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // should throw - ABI_CHECK(callContractFunction("test()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct) -{ - char const* sourceCode = R"( - contract test { - struct testStruct - { - uint m_value; - } - testStruct data1; - testStruct data2; - testStruct data3; - constructor() public - { - data1.m_value = 2; - } - function assign() public returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1) - { - testStruct storage x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 - data2 = data1; // should copy data. data2.m_value == 2 - - ret_local = x.m_value; // = 2 - ret_global = data2.m_value; // = 2 - - x.m_value = 3; - data3 = x; //should copy the data. data3.m_value == 3 - ret_global3 = data3.m_value; // = 3 - ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value - } - } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("assign()"), encodeArgs(2, 2, 3, 3)); -} - -BOOST_AUTO_TEST_CASE(struct_delete_member) -{ - char const* sourceCode = R"( - contract test { - struct testStruct - { - uint m_value; - } - testStruct data1; - constructor() public - { - data1.m_value = 2; - } - function deleteMember() public returns (uint ret_value) - { - testStruct storage x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 - x.m_value = 4; - delete x.m_value; - ret_value = data1.m_value; - } - } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("deleteMember()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping) -{ - char const* sourceCode = R"( - contract test { - struct testStruct - { - uint m_value; - } - mapping (uint => testStruct) campaigns; - - constructor() public - { - campaigns[0].m_value = 2; - } - function deleteIt() public returns (uint) - { - delete campaigns[0]; - return campaigns[0].m_value; - } - } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("deleteIt()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access) -{ - char const* sourceCode = R"( - contract A { - uint[3] arr; - bool public test = false; - function getElement(uint i) public returns (uint) - { - return arr[i]; - } - function testIt() public returns (bool) - { - uint i = this.getElement(5); - test = true; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "A"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); - ABI_CHECK(callContractFunction("testIt()"), encodeArgs()); - ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); -} - -BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail) -{ - char const* sourceCode = R"( - contract A { - constructor() public - { - address(this).call("123"); - } - } - contract B { - uint public test = 1; - function testIt() public - { - A a = new A(); - ++test; - } - } - )"; - compileAndRun(sourceCode, 0, "B"); - - ABI_CHECK(callContractFunction("testIt()"), encodeArgs()); - ABI_CHECK(callContractFunction("test()"), encodeArgs(2)); -} - BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) { char const* sourceCode = R"( @@ -6073,21 +3622,6 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) BOOST_CHECK(!m_transactionSuccessful); } -BOOST_AUTO_TEST_CASE(positive_integers_to_signed) -{ - char const* sourceCode = R"( - contract test { - int8 public x = 2; - int8 public y = 127; - int16 public q = 250; - } - )"; - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("y()"), encodeArgs(127)); - ABI_CHECK(callContractFunction("q()"), encodeArgs(250)); -} - BOOST_AUTO_TEST_CASE(failing_send) { char const* sourceCode = R"( @@ -6111,53 +3645,6 @@ BOOST_AUTO_TEST_CASE(failing_send) BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); } -BOOST_AUTO_TEST_CASE(send_zero_ether) -{ - // Sending zero ether to a contract should still invoke the receive ether function - // (it previously did not because the gas stipend was not provided by the EVM) - char const* sourceCode = R"( - contract Receiver { - receive () external payable { - } - } - contract Main { - constructor() public payable {} - function s() public returns (bool) { - Receiver r = new Receiver(); - return address(r).send(0); - } - } - )"; - compileAndRun(sourceCode, 20, "Main"); - BOOST_REQUIRE(callContractFunction("s()") == encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(reusing_memory) -{ - // Invoke some features that use memory and test that they do not interfere with each other. - char const* sourceCode = R"( - contract Helper { - uint public flag; - constructor(uint x) public { - flag = x; - } - } - contract Main { - mapping(uint => uint) map; - function f(uint x) public returns (uint) { - map[x] = x; - return (new Helper(uint(keccak256(abi.encodePacked(this.g(map[x])))))).flag(); - } - function g(uint a) public returns (uint) - { - return map[a]; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(util::keccak256(util::toBigEndian(u256(0x34))))); -} - BOOST_AUTO_TEST_CASE(return_string) { char const* sourceCode = R"( @@ -6463,23 +3950,6 @@ BOOST_AUTO_TEST_CASE(arrays_in_constructors) ); } -BOOST_AUTO_TEST_CASE(fixed_arrays_in_constructors) -{ - char const* sourceCode = R"( - contract Creator { - uint public r; - address public ch; - constructor(address[3] memory s, uint x) public { - r = x; - ch = s[2]; - } - } - )"; - compileAndRun(sourceCode, 0, "Creator", encodeArgs(u256(1), u256(2), u256(3), u256(4))); - BOOST_REQUIRE(callContractFunction("r()") == encodeArgs(u256(4))); - BOOST_REQUIRE(callContractFunction("ch()") == encodeArgs(u256(3))); -} - BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) { char const* sourceCode = R"( @@ -6507,138 +3977,6 @@ BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data)); } -BOOST_AUTO_TEST_CASE(arrays_complex_from_and_to_storage) -{ - char const* sourceCode = R"( - contract Test { - uint24[3][] public data; - function set(uint24[3][] memory _data) public returns (uint) { - data = _data; - return data.length; - } - function get() public returns (uint24[3][] memory) { - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - BOOST_REQUIRE( - callContractFunction("set(uint24[3][])", u256(0x20), u256(data.size() / 3), data) == - encodeArgs(u256(data.size() / 3)) - ); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(2), u256(2)), encodeArgs(u256(9))); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(5), u256(1)), encodeArgs(u256(17))); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(6), u256(0)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size() / 3), data)); -} - -BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access) -{ - char const* sourceCode = R"( - contract Test { - function set(uint24[3][] memory _data, uint a, uint b) public returns (uint l, uint e) { - l = _data.length; - e = _data[a][b]; - } - } - )"; - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - - BOOST_REQUIRE(callContractFunction( - "set(uint24[3][],uint256,uint256)", - u256(0x60), - u256(3), - u256(2), - u256(data.size() / 3), - data - ) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2]))); - ); -} - -BOOST_AUTO_TEST_CASE(bytes_memory_index_access) -{ - char const* sourceCode = R"( - contract Test { - function set(bytes memory _data, uint i) public returns (uint l, byte c) { - l = _data.length; - c = _data[i]; - } - } - )"; - string data("abcdefgh"); - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - - BOOST_REQUIRE(callContractFunction( - "set(bytes,uint256)", - u256(0x40), - u256(3), - u256(data.size()), - data - ) == encodeArgs(u256(data.size()), string("d"))); - ); -} - -BOOST_AUTO_TEST_CASE(storage_array_ref) -{ - char const* sourceCode = R"( - contract BinarySearch { - /// Finds the position of _value in the sorted list _data. - /// Note that "internal" is important here, because storage references only work for internal or private functions - function find(uint[] storage _data, uint _value) internal returns (uint o_position) { - return find(_data, 0, _data.length, _value); - } - function find(uint[] storage _data, uint _begin, uint _len, uint _value) private returns (uint o_position) { - if (_len == 0 || (_len == 1 && _data[_begin] != _value)) - return uint(-1); // failure - uint halfLen = _len / 2; - uint v = _data[_begin + halfLen]; - if (_value < v) - return find(_data, _begin, halfLen, _value); - else if (_value > v) - return find(_data, _begin + halfLen + 1, halfLen - 1, _value); - else - return _begin + halfLen; - } - } - - contract Store is BinarySearch { - uint[] data; - function add(uint v) public { - data.push(0); - data[data.length - 1] = v; - } - function find(uint v) public returns (uint) { - return find(data, v); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Store"); - BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(-1))); - BOOST_REQUIRE(callContractFunction("add(uint256)", u256(7)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("find(uint256)", u256(7)) == encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("add(uint256)", u256(11)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(17)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(27)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(31)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(32)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(66)), encodeArgs()); - ABI_CHECK(callContractFunction("add(uint256)", u256(177)), encodeArgs()); - ABI_CHECK(callContractFunction("find(uint256)", u256(7)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("find(uint256)", u256(27)), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("find(uint256)", u256(32)), encodeArgs(u256(5))); - ABI_CHECK(callContractFunction("find(uint256)", u256(176)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("find(uint256)", u256(0)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("find(uint256)", u256(400)), encodeArgs(u256(-1))); - ); -} - BOOST_AUTO_TEST_CASE(memory_types_initialisation) { char const* sourceCode = R"( @@ -6690,434 +4028,6 @@ BOOST_AUTO_TEST_CASE(memory_arrays_delete) ABI_CHECK(callContractFunction("del()"), encodeArgs(data)); } -BOOST_AUTO_TEST_CASE(memory_arrays_index_access_write) -{ - char const* sourceCode = R"( - contract Test { - function set(uint24[3][4] memory x) public { - x[2][2] = 1; - x[3][2] = 7; - } - function f() public returns (uint24[3][4] memory){ - uint24[3][4] memory data; - set(data); - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - data[3 * 2 + 2] = 1; - data[3 * 3 + 2] = 7; - ABI_CHECK(callContractFunction("f()"), encodeArgs(data)); -} - -BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write) -{ - char const* sourceCode = R"( - contract Test { - uint24[3][][4] data; - function set(uint24[3][][4] memory x) internal returns (uint24[3][][4] memory) { - x[1][2][2] = 1; - x[1][3][2] = 7; - return x; - } - function f() public returns (uint24[3][] memory) { - while (data[1].length < 4) - data[1].push(); - return set(data)[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - data[3 * 2 + 2] = 1; - data[3 * 3 + 2] = 7; - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(4), data)); -} - -BOOST_AUTO_TEST_CASE(memory_structs_read_write) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; uint8[2] a; } - S[5] data; - function testInit() public returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) { - S[2] memory d; - x = d[0].x; - y = d[0].y; - z = d[0].z; - a = d[0].a[1]; - flag = true; - } - function testCopyRead() public returns (uint8 x, uint16 y, uint z, uint8 a) { - data[2].x = 1; - data[2].y = 2; - data[2].z = 3; - data[2].a[1] = 4; - S memory s = data[2]; - x = s.x; - y = s.y; - z = s.z; - a = s.a[1]; - } - function testAssign() public returns (uint8 x, uint16 y, uint z, uint8 a) { - S memory s; - s.x = 1; - s.y = 2; - s.z = 3; - s.a[1] = 4; - x = s.x; - y = s.y; - z = s.z; - a = s.a[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("testInit()"), encodeArgs(u256(0), u256(0), u256(0), u256(0), true)); - ABI_CHECK(callContractFunction("testCopyRead()"), encodeArgs(u256(1), u256(2), u256(3), u256(4))); - ABI_CHECK(callContractFunction("testAssign()"), encodeArgs(u256(1), u256(2), u256(3), u256(4))); -} - -BOOST_AUTO_TEST_CASE(memory_structs_as_function_args) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - function test() public returns (uint x, uint y, uint z) { - S memory data = combine(1, 2, 3); - x = extract(data, 0); - y = extract(data, 1); - z = extract(data, 2); - } - function extract(S memory s, uint which) internal returns (uint x) { - if (which == 0) return s.x; - else if (which == 1) return s.y; - else return s.z; - } - function combine(uint8 x, uint16 y, uint z) internal returns (S memory s) { - s.x = x; - s.y = y; - s.z = z; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(1), u256(2), u256(3))); -} - -BOOST_AUTO_TEST_CASE(memory_structs_nested) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - struct X { uint8 x; S s; } - function test() public returns (uint a, uint x, uint y, uint z) { - X memory d = combine(1, 2, 3, 4); - a = extract(d, 0); - x = extract(d, 1); - y = extract(d, 2); - z = extract(d, 3); - } - function extract(X memory s, uint which) internal returns (uint x) { - if (which == 0) return s.x; - else if (which == 1) return s.s.x; - else if (which == 2) return s.s.y; - else return s.s.z; - } - function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X memory s) { - s.x = a; - s.s.x = x; - s.s.y = y; - s.s.z = z; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(1), u256(2), u256(3), u256(4))); -} - -BOOST_AUTO_TEST_CASE(memory_structs_nested_load) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - struct X { uint8 x; S s; uint8[2] a; } - X m_x; - function load() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { - m_x.x = 1; - m_x.s.x = 2; - m_x.s.y = 3; - m_x.s.z = 4; - m_x.a[0] = 5; - m_x.a[1] = 6; - X memory d = m_x; - a = d.x; - x = d.s.x; - y = d.s.y; - z = d.s.z; - a1 = d.a[0]; - a2 = d.a[1]; - } - function store() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { - X memory d; - d.x = 1; - d.s.x = 2; - d.s.y = 3; - d.s.z = 4; - d.a[0] = 5; - d.a[1] = 6; - m_x = d; - a = m_x.x; - x = m_x.s.x; - y = m_x.s.y; - z = m_x.s.z; - a1 = m_x.a[0]; - a2 = m_x.a[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - auto out = encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5), u256(6)); - ABI_CHECK(callContractFunction("load()"), out); - ABI_CHECK(callContractFunction("store()"), out); -} - -BOOST_AUTO_TEST_CASE(struct_constructor_nested) -{ - char const* sourceCode = R"( - contract C { - struct X { uint x1; uint x2; } - struct S { uint s1; uint[3] s2; X s3; } - S s; - constructor() public { - uint[3] memory s2; - s2[1] = 9; - s = S(1, s2, X(4, 5)); - } - function get() public returns (uint s1, uint[3] memory s2, uint x1, uint x2) - { - s1 = s.s1; - s2 = s.s2; - x1 = s.s3.x1; - x2 = s.s3.x2; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - auto out = encodeArgs(u256(1), u256(0), u256(9), u256(0), u256(4), u256(5)); - ABI_CHECK(callContractFunction("get()"), out); -} - -BOOST_AUTO_TEST_CASE(struct_named_constructor) -{ - char const* sourceCode = R"( - contract C { - struct S { uint a; bool x; } - S public s; - constructor() public { - s = S({a: 1, x: true}); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("s()"), encodeArgs(u256(1), true)); -} - -BOOST_AUTO_TEST_CASE(calldata_array) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint[2] calldata s) external pure returns (uint256 a, uint256 b) { - a = s[0]; - b = s[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f(uint256[2])", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S calldata s) external pure returns (uint256 a, uint256 b) { - a = s.a; - b = s.b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256))", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_and_ints) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(uint256 a, S calldata s, uint256 b) external pure returns (uint256, uint256, uint256, uint256) { - return (a, s.a, s.b, b); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f(uint256,(uint256,uint256),uint256)", encodeArgs(u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(1), u256(2), u256(3), u256(4))); -} - - -BOOST_AUTO_TEST_CASE(calldata_structs) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S1 { uint256 a; uint256 b; } - struct S2 { uint256 a; } - function f(S1 calldata s1, S2 calldata s2, S1 calldata s3) - external pure returns (uint256 a, uint256 b, uint256 c, uint256 d, uint256 e) { - a = s1.a; - b = s1.b; - c = s2.a; - d = s3.a; - e = s3.b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256),(uint256),(uint256,uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))); -} - -BOOST_AUTO_TEST_CASE(calldata_struct_array_member) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256[2] b; uint256 c; } - function f(S calldata s) external pure returns (uint256 a, uint256 b0, uint256 b1, uint256 c) { - a = s.a; - b0 = s.b[0]; - b1 = s.b[1]; - c = s.c; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256[2],uint256))", encodeArgs(u256(42), u256(1), u256(2), u256(23))), encodeArgs(u256(42), u256(1), u256(2), u256(23))); -} - -BOOST_AUTO_TEST_CASE(calldata_array_of_struct) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S[] calldata s) external pure returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) { - l = s.length; - a = s[0].a; - b = s[0].b; - c = s[1].a; - d = s[1].b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256)[])", encodeArgs(u256(0x20), u256(2), u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(2), u256(1), u256(2), u256(3), u256(4))); -} - -BOOST_AUTO_TEST_CASE(calldata_array_of_struct_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S[] calldata s) external pure returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) { - S[] memory m = s; - l = m.length; - a = m[0].a; - b = m[0].b; - c = m[1].a; - d = m[1].b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256)[])", encodeArgs(u256(0x20), u256(2), u256(1), u256(2), u256(3), u256(4))), encodeArgs(u256(2), u256(1), u256(2), u256(3), u256(4))); -} - - -BOOST_AUTO_TEST_CASE(calldata_struct_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint256 a; uint256 b; } - function f(S calldata s) external pure returns (uint256, uint256) { - S memory m = s; - return (m.a, m.b); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256))", encodeArgs(u256(42), u256(23))), encodeArgs(u256(42), u256(23))); -} - -BOOST_AUTO_TEST_CASE(nested_calldata_struct) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S1 { uint256 a; uint256 b; } - struct S2 { uint256 a; uint256 b; S1 s; uint256 c; } - function f(S2 calldata s) external pure returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) { - return (s.a, s.b, s.s.a, s.s.b, s.c); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256,(uint256,uint256),uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))); -} - -BOOST_AUTO_TEST_CASE(nested_calldata_struct_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S1 { uint256 a; uint256 b; } - struct S2 { uint256 a; uint256 b; S1 s; uint256 c; } - function f(S2 calldata s) external pure returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) { - S2 memory m = s; - return (m.a, m.b, m.s.a, m.s.b, m.c); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK(callContractFunction("f((uint256,uint256,(uint256,uint256),uint256))", encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))), encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5))); -} - BOOST_AUTO_TEST_CASE(calldata_struct_short) { char const* sourceCode = R"( @@ -7141,31 +4051,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_short) ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes()), encodeArgs()); } -BOOST_AUTO_TEST_CASE(calldata_struct_cleaning) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint8 a; bytes1 b; } - function f(S calldata s) external pure returns (uint256 a, bytes32 b) { - uint8 tmp1 = s.a; - bytes1 tmp2 = s.b; - assembly { - a := tmp1 - b := tmp2 - } - - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // double check that the valid case goes through - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x12), bytes{0x34} + bytes(31,0)), encodeArgs(0x12, bytes{0x34} + bytes(31,0))); - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x1234), bytes{0x56, 0x78} + bytes(30,0)), encodeArgs()); - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(-1), u256(-1)), encodeArgs()); -} - BOOST_AUTO_TEST_CASE(calldata_struct_function_type) { char const* sourceCode = R"( @@ -7191,111 +4076,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_function_type) ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23)); } -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_bytes) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f1(bytes[1] calldata a) external returns (uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2])); - } - function f2(bytes[1] calldata a, bytes[1] calldata b) external returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2]), b[0].length, uint8(b[0][0]), uint8(b[0][1])); - } - function g1(bytes[2] calldata a) external returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2]), a[1].length, uint8(a[1][0]), uint8(a[1][1]), uint8(a[1][2])); - } - function g2(bytes[] calldata a) external returns (uint256[8] memory) { - return [a.length, a[0].length, uint8(a[0][0]), uint8(a[0][1]), a[1].length, uint8(a[1][0]), uint8(a[1][1]), uint8(a[1][2])]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes bytes010203 = bytes{1,2,3}+bytes(29,0); - bytes bytes040506 = bytes{4,5,6}+bytes(29,0); - bytes bytes0102 = bytes{1,2}+bytes(30,0); - ABI_CHECK( - callContractFunction("f1(bytes[1])", 0x20, 0x20, 3, bytes010203), - encodeArgs(3, 1, 2, 3) - ); - ABI_CHECK( - callContractFunction("f2(bytes[1],bytes[1])", 0x40, 0xA0, 0x20, 3, bytes010203, 0x20, 2, bytes0102), - encodeArgs(3, 1, 2, 3, 2, 1, 2) - ); - ABI_CHECK( - callContractFunction("g1(bytes[2])", 0x20, 0x40, 0x80, 3, bytes010203, 3, bytes040506), - encodeArgs(3, 1, 2, 3, 3, 4, 5, 6) - ); - // same offset for both arrays - ABI_CHECK( - callContractFunction("g1(bytes[2])", 0x20, 0x40, 0x40, 3, bytes010203), - encodeArgs(3, 1, 2, 3, 3, 1, 2, 3) - ); - ABI_CHECK( - callContractFunction("g2(bytes[])", 0x20, 2, 0x40, 0x80, 2, bytes0102, 3, bytes040506), - encodeArgs(2, 2, 1, 2, 3, 4, 5, 6) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_dynamic_array_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint256[][] calldata a) external returns (uint, uint256[] memory) { - uint256[] memory m = a[0]; - return (a.length, m); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(uint256[][])", 0x20, 1, 0x20, 2, 23, 42), - encodeArgs(1, 0x40, 2, 23, 42) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_bytes_array_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes[] calldata a) external returns (uint, uint, bytes memory) { - bytes memory m = a[0]; - return (a.length, m.length, m); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs(1, 2, 0x60, 2, bytes{'a','b'} + bytes(30, 0)) - ); - - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, bytes(32, 'x')), - encodeArgs(1, 32, 0x60, 32, bytes(32, 'x')) - ); - bytes x_zero_a = bytes{'x'} + bytes(30, 0) + bytes{'a'}; - bytes a_zero_x = bytes{'a'} + bytes(30, 0) + bytes{'x'}; - bytes a_m_x = bytes{'a'} + bytes(30, 'm') + bytes{'x'}; - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, x_zero_a), - encodeArgs(1, 32, 0x60, 32, x_zero_a) - ); - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, a_zero_x), - encodeArgs(1, 32, 0x60, 32, a_zero_x) - ); - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, a_m_x), - encodeArgs(1, 32, 0x60, 32, a_m_x) - ); -} - BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) { char const* sourceCode = R"( @@ -7322,26 +4102,6 @@ BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) ); } -BOOST_AUTO_TEST_CASE(calldata_string_array) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(string[] calldata a) external returns (uint, uint, uint, string memory) { - string memory s1 = a[0]; - bytes memory m1 = bytes(s1); - return (a.length, m1.length, uint8(m1[0]), s1); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(string[])", 0x20, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs(1, 2, 'a', 0x80, 2, bytes{'a', 'b'} + bytes(30, 0)) - ); -} - BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) { vector> data { @@ -7481,70 +4241,6 @@ BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) } } -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_invalid) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint256[][] calldata a) external returns (uint) { - return 42; - } - function g(uint256[][] calldata a) external returns (uint) { - a[0]; - return 42; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // valid access stub - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 0), encodeArgs(42)); - // invalid on argument decoding - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 1), encodeArgs()); - // invalid on outer access - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 1, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][])", 0x20, 1, 0x20), encodeArgs()); - // invalid on inner access - ABI_CHECK(callContractFunction("f(uint256[][])", 0x20, 1, 0x20, 2, 0x42), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][])", 0x20, 1, 0x20, 2, 0x42), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_invalid_static_middle) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(uint256[][1][] calldata a) external returns (uint) { - return 42; - } - function g(uint256[][1][] calldata a) external returns (uint) { - a[0]; - return 42; - } - function h(uint256[][1][] calldata a) external returns (uint) { - a[0][0]; - return 42; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // valid access stub - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 0), encodeArgs(42)); - // invalid on argument decoding - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1), encodeArgs()); - // invalid on outer access - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][1][])", 0x20, 1, 0x20), encodeArgs()); - // invalid on inner access - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1, 0x20, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][1][])", 0x20, 1, 0x20, 0x20), encodeArgs(42)); - ABI_CHECK(callContractFunction("h(uint256[][1][])", 0x20, 1, 0x20, 0x20), encodeArgs()); - ABI_CHECK(callContractFunction("f(uint256[][1][])", 0x20, 1, 0x20, 0x20, 1), encodeArgs(42)); - ABI_CHECK(callContractFunction("g(uint256[][1][])", 0x20, 1, 0x20, 0x20, 1), encodeArgs(42)); - ABI_CHECK(callContractFunction("h(uint256[][1][])", 0x20, 1, 0x20, 0x20, 1), encodeArgs()); -} - BOOST_AUTO_TEST_CASE(literal_strings) { char const* sourceCode = R"( @@ -7591,54 +4287,6 @@ BOOST_AUTO_TEST_CASE(initialise_string_constant) ABI_CHECK(callContractFunction("short()"), encodeDyn(shortStr)); } -BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 a; mapping(uint => uint) b; uint8 c; } - S s; - function f() public returns (uint) { - S memory x; - if (x.a != 0 || x.c != 0) return 1; - x.a = 4; x.c = 5; - s = x; - if (s.a != 4 || s.c != 5) return 2; - x = S(2, 3); - if (x.a != 2 || x.c != 3) return 3; - x = s; - if (s.a != 4 || s.c != 5) return 4; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(string_bytes_conversion) -{ - char const* sourceCode = R"( - contract Test { - string s; - bytes b; - function f(string memory _s, uint n) public returns (byte) { - b = bytes(_s); - s = string(b); - return bytes(s)[n]; - } - function l() public returns (uint) { return bytes(s).length; } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction( - "f(string,uint256)", - u256(0x40), - u256(2), - u256(6), - string("abcdef") - ), encodeArgs("c")); - ABI_CHECK(callContractFunction("l()"), encodeArgs(u256(6))); -} - BOOST_AUTO_TEST_CASE(string_as_mapping_key) { char const* sourceCode = R"( @@ -7814,162 +4462,6 @@ BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key) ), encodeArgs(u256(i - 3))); } -BOOST_AUTO_TEST_CASE(accessor_for_state_variable) -{ - char const* sourceCode = R"( - contract Lotto { - uint public ticketPrice = 500; - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(500))); - ); -} - -BOOST_AUTO_TEST_CASE(accessor_for_const_state_variable) -{ - char const* sourceCode = R"( - contract Lotto{ - uint constant public ticketPrice = 555; - } - )"; - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("ticketPrice()"), encodeArgs(u256(555))); -} - -BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) -{ - char const* text = R"( - contract Scope { - uint stateVar = 42; - - function getStateVar() public view returns (uint stateVar) { - stateVar = Scope.stateVar; - } - } - )"; - compileAndRun(text); - ABI_CHECK(callContractFunction("getStateVar()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(state_variable_local_variable_mixture) -{ - char const* sourceCode = R"( - contract A { - uint x = 1; - uint y = 2; - function a() public returns (uint x) { - x = A.y; - } - } - )"; - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(inherited_function) { - char const* sourceCode = R"( - contract A { function f() virtual internal returns (uint) { return 1; } } - contract B is A { - function f() internal override returns (uint) { return 2; } - function g() public returns (uint) { - return A.f(); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_calldata_memory) { - char const* sourceCode = R"( - contract A { function f(uint[] calldata a) virtual external returns (uint) { return a[0]; } } - contract B is A { - function f(uint[] memory a) public override returns (uint) { return a[1]; } - function g() public returns (uint) { - uint[] memory m = new uint[](2); - m[0] = 42; - m[1] = 23; - return A(this).f(m); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(23))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_calldata_memory_interface) { - char const* sourceCode = R"( - interface I { function f(uint[] calldata a) external returns (uint); } - contract A is I { function f(uint[] memory a) public override returns (uint) { return 42; } } - contract B { - function f(uint[] memory a) public returns (uint) { return a[1]; } - function g() public returns (uint) { - I i = I(new A()); - return i.f(new uint[](2)); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_calldata_calldata_interface) { - char const* sourceCode = R"( - interface I { function f(uint[] calldata a) external returns (uint); } - contract A is I { function f(uint[] calldata a) external override returns (uint) { return 42; } } - contract B { - function f(uint[] memory a) public returns (uint) { return a[1]; } - function g() public returns (uint) { - I i = I(new A()); - return i.f(new uint[](2)); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(42))); -} - -BOOST_AUTO_TEST_CASE(inherited_function_from_a_library) { - char const* sourceCode = R"( - library A { function f() internal returns (uint) { return 1; } } - contract B { - function f() internal returns (uint) { return 2; } - function g() public returns (uint) { - return A.f(); - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(inherited_constant_state_var) -{ - char const* sourceCode = R"( - contract A { - uint constant x = 7; - } - contract B is A { - function f() public returns (uint) { - return A.x; - } - } - )"; - - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - BOOST_AUTO_TEST_CASE(constant_string_literal) { char const* sourceCode = R"( @@ -8002,21 +4494,6 @@ BOOST_AUTO_TEST_CASE(constant_string_literal) ABI_CHECK(callContractFunction("unused()"), encodeArgs(2)); } -BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable) -{ - char const* sourceCode = R"( - contract Test { - mapping(string => uint) data; - function f() public returns (uint) { - data["abc"] = 2; - return data["abc"]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - BOOST_AUTO_TEST_CASE(library_call) { char const* sourceCode = R"( @@ -8064,110 +4541,6 @@ BOOST_AUTO_TEST_CASE(library_stray_values) ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(42))); } -BOOST_AUTO_TEST_CASE(cross_contract_types) -{ - char const* sourceCode = R"( - contract Lib { struct S {uint a; uint b; } } - contract Test { - function f() public returns (uint r) { - Lib.S memory x = Lib.S({a: 2, b: 3}); - r = x.b; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); -} - -BOOST_AUTO_TEST_CASE(simple_throw) -{ - char const* sourceCode = R"( - contract Test { - function f(uint x) public returns (uint) { - if (x > 10) - return x + 10; - else - revert(); - return 2; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(11)), encodeArgs(u256(21))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(strings_in_struct) -{ - char const* sourceCode = R"( - contract buggystruct { - Buggy public bug; - - struct Buggy { - uint first; - uint second; - uint third; - string last; - } - - constructor() public { - bug = Buggy(10, 20, 30, "asdfghjkl"); - } - function getFirst() public returns (uint) - { - return bug.first; - } - function getSecond() public returns (uint) - { - return bug.second; - } - function getThird() public returns (uint) - { - return bug.third; - } - function getLast() public returns (string memory) - { - return bug.last; - } - } - )"; - compileAndRun(sourceCode); - string s = "asdfghjkl"; - ABI_CHECK(callContractFunction("getFirst()"), encodeArgs(u256(10))); - ABI_CHECK(callContractFunction("getSecond()"), encodeArgs(u256(20))); - ABI_CHECK(callContractFunction("getThird()"), encodeArgs(u256(30))); - ABI_CHECK(callContractFunction("getLast()"), encodeDyn(s)); -} - -BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type) -{ - char const* sourceCode = R"( - contract A { - function f(uint16 input) public pure returns (uint16[5] memory arr) - { - arr[0] = input; - arr[1] = ++input; - arr[2] = ++input; - arr[3] = ++input; - arr[4] = ++input; - } - } - contract B { - function f() public returns (uint16[5] memory res, uint16[5] memory res2) - { - A a = new A(); - res = a.f(2); - res2 = a.f(1000); - } - } - )"; - compileAndRun(sourceCode, 0, "B"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - u256(2), u256(3), u256(4), u256(5), u256(6), // first return argument - u256(1000), u256(1001), u256(1002), u256(1003), u256(1004)) // second return argument - ); -} - BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( @@ -8470,48 +4843,6 @@ BOOST_AUTO_TEST_CASE(using_library_structs) ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7), u256(8))); } -BOOST_AUTO_TEST_CASE(library_struct_as_an_expression) -{ - char const* sourceCode = R"( - library Arst { - struct Foo { - int Things; - int Stuff; - } - } - - contract Tsra { - function f() public returns(uint) { - Arst.Foo; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "Tsra"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(library_enum_as_an_expression) -{ - char const* sourceCode = R"( - library Arst { - enum Foo { - Things, - Stuff - } - } - - contract Tsra { - function f() public returns(uint) { - Arst.Foo; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "Tsra"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - BOOST_AUTO_TEST_CASE(short_strings) { // This test verifies that the byte array encoding that combines length and data works @@ -8649,16 +4980,6 @@ BOOST_AUTO_TEST_CASE(calldata_offset) ABI_CHECK(callContractFunction("last()", encodeArgs()), encodeDyn(string("nd"))); } -BOOST_AUTO_TEST_CASE(contract_binary_dependencies) -{ - char const* sourceCode = R"( - contract A { function f() public { new B(); } } - contract B { function f() public { } } - contract C { function f() public { new B(); } } - )"; - compileAndRun(sourceCode); -} - BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) { char const* sourceCode = R"( @@ -8684,234 +5005,6 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); } -BOOST_AUTO_TEST_CASE(multi_variable_declaration) -{ - char const* sourceCode = R"( - contract C { - function g() public returns (uint a, uint b, uint c) { - a = 1; b = 2; c = 3; - } - function h() public returns (uint a, uint b, uint c, uint d) { - a = 1; b = 2; c = 3; d = 4; - } - function f1() public returns (bool) { - (uint x, uint y, uint z) = g(); - if (x != 1 || y != 2 || z != 3) return false; - (, uint a,) = g(); - if (a != 2) return false; - (uint b, , ) = g(); - if (b != 1) return false; - (, , uint c) = g(); - if (c != 3) return false; - return true; - } - function f2() public returns (bool) { - (uint a1, , uint a3, ) = h(); - if (a1 != 1 || a3 != 3) return false; - (uint b1, uint b2, , ) = h(); - if (b1 != 1 || b2 != 2) return false; - (, uint c2, uint c3, ) = h(); - if (c2 != 2 || c3 != 3) return false; - (, , uint d3, uint d4) = h(); - if (d3 != 3 || d4 != 4) return false; - (uint e1, , uint e3, uint e4) = h(); - if (e1 != 1 || e3 != 3 || e4 != 4) return false; - return true; - } - function f() public returns (bool) { - return f1() && f2(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()", encodeArgs()), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(typed_multi_variable_declaration) -{ - char const* sourceCode = R"( - contract C { - struct S { uint x; } - S s; - function g() internal returns (uint, S storage, uint) { - s.x = 7; - return (1, s, 2); - } - function f() public returns (bool) { - (uint x1, S storage y1, uint z1) = g(); - if (x1 != 1 || y1.x != 7 || z1 != 2) return false; - (, S storage y2,) = g(); - if (y2.x != 7) return false; - (uint x2,,) = g(); - if (x2 != 1) return false; - (,,uint z2) = g(); - if (z2 != 2) return false; - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()", encodeArgs()), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(tuples) -{ - char const* sourceCode = R"( - contract C { - uint[] data; - uint[] m_c; - function g() internal returns (uint a, uint b, uint[] storage c) { - return (1, 2, data); - } - function h() external returns (uint a, uint b) { - return (5, 6); - } - function f() public returns (uint) { - data.push(3); - uint a; uint b; - (a, b) = this.h(); - if (a != 5 || b != 6) return 1; - uint[] storage c = m_c; - (a, b, c) = g(); - if (a != 1 || b != 2 || c[0] != 3) return 2; - (a, b) = (b, a); - if (a != 2 || b != 1) return 3; - (a, , b, , ) = (8, 9, 10, 11, 12); - if (a != 8 || b != 10) return 4; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(string_tuples) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (string memory, uint) { - return ("abc", 8); - } - function g() public returns (string memory, string memory) { - return (h(), "def"); - } - function h() public returns (string memory) { - return ("abc"); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x40), u256(8), u256(3), string("abc"))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0x40), u256(0x80), u256(3), string("abc"), u256(3), string("def"))); -} - -BOOST_AUTO_TEST_CASE(decayed_tuple) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - uint x = 1; - (x) = 2; - return x; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(inline_tuple_with_rational_numbers) -{ - char const* sourceCode = R"( - contract c { - function f() public returns (int8) { - int8[5] memory foo3 = [int8(1), -1, 0, 0, 0]; - return foo3[0]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(destructuring_assignment) -{ - char const* sourceCode = R"( - contract C { - uint x = 7; - bytes data; - uint[] y; - uint[] arrayData; - function returnsArray() public returns (uint[] memory) { - arrayData = new uint[](9); - arrayData[2] = 5; - arrayData[7] = 4; - return arrayData; - } - function f(bytes memory s) public returns (uint) { - uint loc; - uint[] memory memArray; - (loc, x, y, data, arrayData[3]) = (8, 4, returnsArray(), s, 2); - if (loc != 8) return 1; - if (x != 4) return 2; - if (y.length != 9) return 3; - if (y[2] != 5) return 4; - if (y[7] != 4) return 5; - if (data.length != s.length) return 6; - if (data[3] != s[3]) return 7; - if (arrayData[3] != 2) return 8; - (memArray, loc) = (arrayData, 3); - if (loc != 3) return 9; - if (memArray.length != arrayData.length) return 10; - bytes memory memBytes; - (x, memBytes, y[2], , ) = (456, s, 789, 101112, 131415); - if (x != 456 || memBytes.length != s.length || y[2] != 789) return 11; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), string("abcde")), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(lone_struct_array_type) -{ - char const* sourceCode = R"( - contract C { - struct s { uint a; uint b;} - function f() public returns (uint) { - s[7][]; // This is only the type, should not have any effect - return 3; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); - ) -} - -BOOST_AUTO_TEST_CASE(create_memory_array) -{ - char const* sourceCode = R"( - contract C { - struct S { uint[2] a; bytes b; } - function f() public returns (byte, uint, uint, byte) { - bytes memory x = new bytes(200); - x[199] = 'A'; - uint[2][] memory y = new uint[2][](300); - y[203][1] = 8; - S[] memory z = new S[](180); - z[170].a[1] = 4; - z[170].b = new bytes(102); - z[170].b[99] = 'B'; - return (x[199], y[203][1], z[170].a[1], z[170].b[99]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(string("A"), u256(8), u256(4), string("B"))); -} - BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) { // Check allocation size of byte array. Should be 32 plus length rounded up to next @@ -8939,175 +5032,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) } } -BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) -{ - // Computes binomial coefficients the chinese way - char const* sourceCode = R"( - contract C { - function f(uint n, uint k) public returns (uint) { - uint[][] memory rows = new uint[][](n + 1); - for (uint i = 1; i <= n; i++) { - rows[i] = new uint[](i); - rows[i][0] = rows[i][rows[i].length - 1] = 1; - for (uint j = 1; j < i - 1; j++) - rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j]; - } - return rows[n][k - 1]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(3), u256(1))), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70))); -} - -BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - uint[][] memory x = new uint[][](42); - assert(x[0].length == 0); - x[0] = new uint[](1); - x[0][0] = 1; - assert(x[4].length == 0); - x[4] = new uint[](1); - x[4][0] = 2; - assert(x[10].length == 0); - x[10] = new uint[](1); - x[10][0] = 44; - uint[][] memory y = new uint[][](24); - assert(y[0].length == 0); - y[0] = new uint[](1); - y[0][0] = 1; - assert(y[4].length == 0); - y[4] = new uint[](1); - y[4][0] = 2; - assert(y[10].length == 0); - y[10] = new uint[](1); - y[10][0] = 88; - if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88)) - return 7; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(memory_overwrite) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (bytes memory x) { - x = "12345"; - x[3] = 0x61; - x[0] = 0x62; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeDyn(string("b23a5"))); -} - -BOOST_AUTO_TEST_CASE(addmod_mulmod) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (uint) { - // Note that this only works because computation on literals is done using - // unbounded integers. - if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) - return 1; - if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) - return 2; - return 0; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) -{ - char const* sourceCode = R"( - contract C { - function f(uint d) public pure returns (uint) { - addmod(1, 2, d); - return 2; - } - function g(uint d) public pure returns (uint) { - mulmod(1, 2, d); - return 2; - } - function h() public pure returns (uint) { - mulmod(0, 1, 2); - mulmod(1, 0, 2); - addmod(0, 1, 2); - addmod(1, 0, 2); - return 2; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint)", 0), encodeArgs()); - ABI_CHECK(callContractFunction("g(uint)", 0), encodeArgs()); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); -} - -BOOST_AUTO_TEST_CASE(divisiod_by_zero) -{ - char const* sourceCode = R"( - contract C { - function div(uint a, uint b) public returns (uint) { - return a / b; - } - function mod(uint a, uint b) public returns (uint) { - return a % b; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("div(uint256,uint256)", 7, 2), encodeArgs(u256(3))); - // throws - ABI_CHECK(callContractFunction("div(uint256,uint256)", 7, 0), encodeArgs()); - ABI_CHECK(callContractFunction("mod(uint256,uint256)", 7, 2), encodeArgs(u256(1))); - // throws - ABI_CHECK(callContractFunction("mod(uint256,uint256)", 7, 0), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(string_allocation_bug) -{ - char const* sourceCode = R"( - contract Sample - { - struct s { uint16 x; uint16 y; string a; string b;} - s[2] public p; - constructor() public { - s memory m; - m.x = 0xbbbb; - m.y = 0xcccc; - m.a = "hello"; - m.b = "world"; - p[0] = m; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("p(uint256)", 0), encodeArgs( - u256(0xbbbb), - u256(0xcccc), - u256(0x80), - u256(0xc0), - u256(5), - string("hello"), - u256(5), - string("world") - )); -} - BOOST_AUTO_TEST_CASE(using_for_function_on_int) { char const* sourceCode = R"( @@ -9246,119 +5170,6 @@ BOOST_AUTO_TEST_CASE(bound_function_to_string) ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(3))); } -BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_strings) -{ - char const* sourceCode = R"( - contract C { - string s = "doh"; - function f() public returns (string memory, string memory) { - string memory t = "ray"; - string[3] memory x = [s, t, "mi"]; - return (x[1], x[2]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x40), u256(0x80), u256(3), string("ray"), u256(2), string("mi"))); -} - -BOOST_AUTO_TEST_CASE(inline_array_strings_from_document) -{ - char const* sourceCode = R"( - contract C { - function f(uint i) public returns (string memory) { - string[4] memory x = ["This", "is", "an", "array"]; - return (x[i]); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(0x20), u256(4), string("This"))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(0x20), u256(2), string("is"))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(0x20), u256(2), string("an"))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(0x20), u256(5), string("array"))); -} - -BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_ints) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint x, uint y) { - x = 3; - y = 6; - uint[2] memory z = [x, y]; - return (z[0], z[1]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3, 6)); -} - -BOOST_AUTO_TEST_CASE(inline_array_index_access_ints) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return ([1, 2, 3, 4][2]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); -} - -BOOST_AUTO_TEST_CASE(inline_array_index_access_strings) -{ - char const* sourceCode = R"( - contract C { - string public tester; - function f() public returns (string memory) { - return (["abc", "def", "g"][0]); - } - function test() public { - tester = f(); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - ABI_CHECK(callContractFunction("tester()"), encodeArgs(u256(0x20), u256(3), string("abc"))); -} - -BOOST_AUTO_TEST_CASE(inline_array_return) -{ - char const* sourceCode = R"( - contract C { - uint8[] tester; - function f() public returns (uint8[5] memory) { - return ([1,2,3,4,5]); - } - function test() public returns (uint8, uint8, uint8, uint8, uint8) { - tester = f(); - return (tester[0], tester[1], tester[2], tester[3], tester[4]); - } - - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1, 2, 3, 4, 5)); -} - -BOOST_AUTO_TEST_CASE(inline_array_singleton) -{ - // This caused a failure since the type was not converted to its mobile type. - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return [4][0]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(4))); -} - BOOST_AUTO_TEST_CASE(inline_long_string_return) { char const* sourceCode = R"( @@ -9374,425 +5185,6 @@ BOOST_AUTO_TEST_CASE(inline_long_string_return) ABI_CHECK(callContractFunction("f()"), encodeDyn(strLong)); } -BOOST_AUTO_TEST_CASE(fixed_bytes_index_access) -{ - char const* sourceCode = R"( - contract C { - bytes16[] public data; - function f(bytes32 x) public returns (byte) { - return x[2]; - } - function g(bytes32 x) public returns (uint) { - data = [x[0], x[1], x[2]]; - data[0] = "12345"; - return uint(uint8(data[0][4])); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(bytes32)", "789"), encodeArgs("9")); - ABI_CHECK(callContractFunction("g(bytes32)", "789"), encodeArgs(u256(int('5')))); - ABI_CHECK(callContractFunction("data(uint256)", u256(1)), encodeArgs("8")); -} - -BOOST_AUTO_TEST_CASE(fixed_bytes_length_access) -{ - char const* sourceCode = R"( - contract C { - byte a; - function f(bytes32 x) public returns (uint, uint, uint) { - return (x.length, bytes16(uint128(2)).length, a.length + 7); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(bytes32)", "789"), encodeArgs(u256(32), u256(16), u256(8))); -} - -BOOST_AUTO_TEST_CASE(byte_optimization_bug) -{ - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint a) { - assembly { - a := byte(x, 31) - } - } - function g(uint x) public returns (uint a) { - assembly { - a := byte(31, x) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("g(uint256)", u256(2)), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_write_to_stack) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint r, bytes32 r2) { - assembly { r := 7 r2 := "abcdef" } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7), string("abcdef"))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_read_and_write_stack) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint r) { - for (uint x = 0; x < 10; ++x) - assembly { r := add(r, x) } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(45))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_memory_access) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (bytes memory) { - bytes memory x = new bytes(5); - for (uint i = 0; i < x.length; ++i) - x[i] = byte(uint8(i + 1)); - assembly { mstore(add(x, 32), "12345678901234567890123456789012") } - return x; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0x20), u256(5), string("12345"))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_access) -{ - char const* sourceCode = R"( - contract C { - uint16 x; - uint16 public y; - uint public z; - function f() public returns (bool) { - uint off1; - uint off2; - assembly { - sstore(z_slot, 7) - off1 := z_offset - off2 := y_offset - } - assert(off1 == 0); - assert(off2 == 2); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("z()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_inside_function) -{ - char const* sourceCode = R"( - contract C { - uint16 x; - uint16 public y; - uint public z; - function f() public returns (bool) { - uint off1; - uint off2; - assembly { - function f() -> o1 { - sstore(z_slot, 7) - o1 := y_offset - } - off2 := f() - } - assert(off2 == 2); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("z()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_via_pointer) -{ - char const* sourceCode = R"( - contract C { - struct Data { uint contents; } - uint public separator; - Data public a; - uint public separator2; - function f() public returns (bool) { - Data storage x = a; - uint off; - assembly { - sstore(x_slot, 7) - off := x_offset - } - assert(off == 0); - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("separator()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("separator2()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_call) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - function asmfun(a, b, c) -> x, y, z { - x := a - y := b - z := 7 - } - let a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - return(0, 0x60) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_call_assignment) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - let a1, b1, c1 - function asmfun(a, b, c) -> x, y, z { - x := a - y := b - z := 7 - } - a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - return(0, 0x60) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_call2) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - let d := 0x10 - function asmfun(a, b, c) -> x, y, z { - x := a - y := b - z := 7 - } - let a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - mstore(0x60, d) - return(0, 0x80) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(2), u256(7), u256(0x10))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_embedded_function_call) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - let d := 0x10 - function asmfun(a, b, c) -> x, y, z { - x := g(a) - function g(r) -> s { s := mul(r, r) } - y := g(b) - z := 7 - } - let a1, b1, c1 := asmfun(1, 2, 3) - mstore(0x00, a1) - mstore(0x20, b1) - mstore(0x40, c1) - mstore(0x60, d) - return(0, 0x80) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(4), u256(7), u256(0x10))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_if) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - if gt(a, 1) { b := 2 } - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_switch) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - switch a - case 1 { b := 8 } - case 2 { b := 9 } - default { b := 2 } - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(9))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_recursion) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - function fac(n) -> nf { - switch n - case 0 { nf := 1 } - case 1 { nf := 1 } - default { nf := mul(n, fac(sub(n, 1))) } - } - b := fac(a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("f(uint256)", u256(4)), encodeArgs(u256(24))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_for) -{ - char const* sourceCode = R"( - contract C { - function f(uint a) public returns (uint b) { - assembly { - function fac(n) -> nf { - nf := 1 - for { let i := n } gt(i, 0) { i := sub(i, 1) } { - nf := mul(nf, i) - } - } - b := fac(a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("f(uint256)", u256(3)), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("f(uint256)", u256(4)), encodeArgs(u256(24))); - ) -} - -BOOST_AUTO_TEST_CASE(inline_assembly_for2) -{ - char const* sourceCode = R"( - contract C { - uint st; - function f(uint a) public returns (uint b, uint c, uint d) { - st = 0; - assembly { - function sideeffect(r) -> x { sstore(0, add(sload(0), r)) x := 1} - for { let i := a } eq(i, sideeffect(2)) { d := add(d, 3) } { - b := i - i := 0 - } - } - c = st; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(0)), encodeArgs(u256(0), u256(2), u256(0))); - ABI_CHECK(callContractFunction("f(uint256)", u256(1)), encodeArgs(u256(1), u256(4), u256(3))); - ABI_CHECK(callContractFunction("f(uint256)", u256(2)), encodeArgs(u256(0), u256(2), u256(0))); - ) -} - BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) { // Test for a bug where higher order bits cleanup was not done for array index access. @@ -9809,284 +5201,6 @@ BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) BOOST_CHECK(callContractFunction("f(uint256)", u256(0x101)).size() == 256 * 32); } -BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) -{ - // Test for a bug where we did not increment the counter properly while deleting a dynamic array. - char const* sourceCode = R"( - contract C { - struct S { uint x; uint[] y; } - S[] data; - function f() public returns (bool) { - S storage s1 = data.push(); - s1.x = 2**200; - S storage s2 = data.push(); - s2.x = 2**200; - delete data; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // This code interprets x as an array length and thus will go out of gas. - // neither of the two should throw due to out-of-bounds access - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - -} - -BOOST_AUTO_TEST_CASE(internal_library_function) -{ - // tests that internal library functions can be called from outside - // and retain the same memory context (i.e. are pulled into the caller's code) - char const* sourceCode = R"( - library L { - function f(uint[] memory _data) internal { - _data[3] = 2; - } - } - contract C { - function f() public returns (uint) { - uint[] memory x = new uint[](7); - x[3] = 8; - L.f(x); - return x[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(internal_library_function_calling_private) -{ - // tests that internal library functions that are called from outside and that - // themselves call private functions are still able to (i.e. the private function - // also has to be pulled into the caller's code) - char const* sourceCode = R"( - library L { - function g(uint[] memory _data) private { - _data[3] = 2; - } - function f(uint[] memory _data) internal { - g(_data); - } - } - contract C { - function f() public returns (uint) { - uint[] memory x = new uint[](7); - x[3] = 8; - L.f(x); - return x[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(internal_library_function_bound) -{ - char const* sourceCode = R"( - library L { - struct S { uint[] data; } - function f(S memory _s) internal { - _s.data[3] = 2; - } - } - contract C { - using L for L.S; - function f() public returns (uint) { - L.S memory x; - x.data = new uint[](7); - x.data[3] = 8; - x.f(); - return x.data[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(internal_library_function_return_var_size) -{ - char const* sourceCode = R"( - library L { - struct S { uint[] data; } - function f(S memory _s) internal returns (uint[] memory) { - _s.data[3] = 2; - return _s.data; - } - } - contract C { - using L for L.S; - function f() public returns (uint) { - L.S memory x; - x.data = new uint[](7); - x.data[3] = 8; - return x.f()[3]; - } - } - )"; - // This has to work without linking, because everything will be inlined. - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(iszero_bnot_correct) -{ - // A long time ago, some opcodes were renamed, which involved the opcodes - // "iszero" and "not". - char const* sourceCode = R"( - contract C { - function f() public returns (bool) { - bytes32 x = bytes32(uint256(1)); - assembly { x := not(x) } - if (x != ~bytes32(uint256(1))) return false; - assembly { x := iszero(x) } - if (x != bytes32(0)) return false; - return true; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(cleanup_bytes_types) -{ - // Checks that bytesXX types are properly cleaned before they are compared. - char const* sourceCode = R"( - contract C { - function f(bytes2 a, uint16 x) public returns (uint) { - if (a != "ab") return 1; - if (x != 0x0102) return 2; - if (bytes3(uint24(x)) != 0x000102) return 3; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // We input longer data on purpose. - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)), v2 ? encodeArgs() : encodeArgs(0)); -} -BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (bytes32 r) { - bytes4 x = 0xffffffff; - bytes2 y = bytes2(x); - assembly { r := y } - // At this point, r and y both store four bytes, but - // y is properly cleaned before the equality check - require(y == bytes2(0xffff)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs("\xff\xff\xff\xff")); -} -BOOST_AUTO_TEST_CASE(cleanup_address_types) -{ - // Checks that address types are properly cleaned before they are compared. - char const* sourceCode = R"( - contract C { - function f(address a) public returns (uint) { - if (a != 0x1234567890123456789012345678901234567890) return 1; - return 0; - } - function g(address payable a) public returns (uint) { - if (a != 0x1234567890123456789012345678901234567890) return 1; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - // We input longer data on purpose. - ABI_CHECK(callContractFunction("f(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); - ABI_CHECK(callContractFunction("g(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(cleanup_address_types_shortening) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (address r) { - bytes21 x = 0x1122334455667788990011223344556677889900ff; - bytes20 y; - assembly { y := x } - address z = address(y); - assembly { r := z } - require(z == 0x1122334455667788990011223344556677889900); - } - function g() public pure returns (address payable r) { - bytes21 x = 0x1122334455667788990011223344556677889900ff; - bytes20 y; - assembly { y := x } - address payable z = address(y); - assembly { r := z } - require(z == 0x1122334455667788990011223344556677889900); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256("0x1122334455667788990011223344556677889900"))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256("0x1122334455667788990011223344556677889900"))); - ) -} - -BOOST_AUTO_TEST_CASE(skip_dynamic_types) -{ - // The EVM cannot provide access to dynamically-sized return values, so we have to skip them. - char const* sourceCode = R"( - contract C { - function f() public returns (uint, uint[] memory, uint) { - return (7, new uint[](2), 8); - } - function g() public returns (uint, uint) { - // Previous implementation "moved" b to the second place and did not skip. - (uint a,, uint b) = this.f(); - return (a, b); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(7), u256(8))); -} - -BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) -{ - // For accessors, the dynamic types are already removed in the external signature itself. - char const* sourceCode = R"( - contract C { - struct S { - uint x; - string a; // this is present in the accessor - uint[] b; // this is not present - uint y; - } - S public s; - function g() public returns (uint, uint) { - s.x = 2; - s.a = "abc"; - s.b = [7, 8, 9]; - s.y = 6; - (uint x,, uint y) = this.s(); - return (x, y); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(2), u256(6))); -} - BOOST_AUTO_TEST_CASE(failed_create) { char const* sourceCode = R"( @@ -10115,20 +5229,6 @@ BOOST_AUTO_TEST_CASE(failed_create) ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); } -BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - uint[][] memory a = new uint[][](0); - return 7; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); -} - BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) { // Memory arrays are initialized using calldatacopy past the size of the calldata. @@ -10159,122 +5259,6 @@ BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) } } -BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier setsx { - _; - x = 9; - } - function f() setsx public returns (uint) { - return 2; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(9))); -} - -BOOST_AUTO_TEST_CASE(break_in_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 0; i < 10; i++) { - _; - break; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(continue_in_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 0; i < 10; i++) { - if (i % 2 == 1) continue; - _; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(5))); -} - -BOOST_AUTO_TEST_CASE(return_in_modifier) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 1; i < 10; i++) { - if (i == 5) return; - _; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers) -{ - char const* sourceCode = R"( - contract C { - uint public x; - modifier run() { - for (uint i = 0; i < 10; i++) { - _; - break; - } - } - function f() run public { - uint k = x; - uint t = k + 1; - x = t; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); -} - BOOST_AUTO_TEST_CASE(mutex) { char const* sourceCode = R"( @@ -10344,44 +5328,6 @@ BOOST_AUTO_TEST_CASE(mutex) BOOST_CHECK_EQUAL(balanceAt(fund), 460); } -BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws) -{ - char const* sourceCode = R"YY( - abstract contract D { function g() public virtual; } - contract C { - D d = D(0x1212); - function f() public returns (uint) { - d.g(); - return 7; - } - function g() public returns (uint) { - d.g.gas(200)(); - return 7; - } - function h() public returns (uint) { - address(d).call(""); // this does not throw (low-level) - return 7; - } - } - )YY"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); - ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(payable_constructor) -{ - char const* sourceCode = R"( - contract C { - constructor() public payable { } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 27, "C"); - ) -} - BOOST_AUTO_TEST_CASE(payable_function) { char const* sourceCode = R"( @@ -10496,111 +5442,6 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) ABI_CHECK(callContractFunction("f(address)", cAddr), encodeArgs(u256(7))); } -BOOST_AUTO_TEST_CASE(calling_uninitialized_function) -{ - char const* sourceCode = R"( - contract C { - function intern() public returns (uint) { - function (uint) internal returns (uint) x; - x(2); - return 7; - } - function extern() public returns (uint) { - function (uint) external returns (uint) x; - x(2); - return 7; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - // This should throw exceptions - ABI_CHECK(callContractFunction("intern()"), encodeArgs()); - ABI_CHECK(callContractFunction("extern()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) -{ - char const* sourceCode = R"( - contract C { - function() internal returns (uint) x; - int mutex; - function t() public returns (uint) { - if (mutex > 0) - { assembly { mstore(0, 7) return(0, 0x20) } } - mutex = 1; - // Avoid re-executing this function if we jump somewhere. - x(); - return 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array) -{ - char const* sourceCode = R"( - contract C { - int mutex; - function t() public returns (uint) { - if (mutex > 0) - { assembly { mstore(0, 7) return(0, 0x20) } } - mutex = 1; - // Avoid re-executing this function if we jump somewhere. - function() internal returns (uint)[200] memory x; - x[0](); - return 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(pass_function_types_internally) -{ - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint) { - return eval(g, x); - } - function eval(function(uint) internal returns (uint) x, uint a) internal returns (uint) { - return x(a); - } - function g(uint x) public returns (uint) { return x + 1; } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", 7), encodeArgs(u256(8))); -} - -BOOST_AUTO_TEST_CASE(pass_function_types_externally) -{ - char const* sourceCode = R"( - contract C { - function f(uint x) public returns (uint) { - return this.eval(this.g, x); - } - function f2(uint x) public returns (uint) { - return eval(this.g, x); - } - function eval(function(uint) external returns (uint) x, uint a) public returns (uint) { - return x(a); - } - function g(uint x) public returns (uint) { return x + 1; } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", 7), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f2(uint256)", 7), encodeArgs(u256(8))); -} - BOOST_AUTO_TEST_CASE(receive_external_function_type) { char const* sourceCode = R"( @@ -10637,1085 +5478,8 @@ BOOST_AUTO_TEST_CASE(return_external_function_type) ); } -BOOST_AUTO_TEST_CASE(store_function) -{ - char const* sourceCode = R"( - contract Other { - function addTwo(uint x) public returns (uint) { return x + 2; } - } - contract C { - function (function (uint) external returns (uint)) internal returns (uint) ev; - function (uint) external returns (uint) x; - function store(function(uint) external returns (uint) y) public { - x = y; - } - function eval(function(uint) external returns (uint) y) public returns (uint) { - return y(7); - } - function t() public returns (uint) { - ev = eval; - this.store((new Other()).addTwo); - return ev(x); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs(u256(9))); -} - -BOOST_AUTO_TEST_CASE(store_function_in_constructor) -{ - char const* sourceCode = R"( - contract C { - uint public result_in_constructor; - function (uint) internal returns (uint) x; - constructor() public { - x = double; - result_in_constructor = use(2); - } - function double(uint _arg) public returns (uint _ret) { - _ret = _arg * 2; - } - function use(uint _arg) public returns (uint) { - return x(_arg); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("use(uint256)", encodeArgs(u256(3))), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("result_in_constructor()"), encodeArgs(u256(4))); -} - // TODO: store bound internal library functions -BOOST_AUTO_TEST_CASE(store_internal_unused_function_in_constructor) -{ - char const* sourceCode = R"( - contract C { - function () internal returns (uint) x; - constructor() public { - x = unused; - } - function unused() internal returns (uint) { - return 7; - } - function t() public returns (uint) { - return x(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(store_internal_unused_library_function_in_constructor) -{ - char const* sourceCode = R"( - library L { function x() internal returns (uint) { return 7; } } - contract C { - function () internal returns (uint) x; - constructor() public { - x = L.x; - } - function t() public returns (uint) { - return x(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("t()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime) -{ - char const* sourceCode = R"( - contract C { - uint public initial; - constructor() public { - initial = double(2); - } - function double(uint _arg) public returns (uint _ret) { - _ret = _arg * 2; - } - function runtime(uint _arg) public returns (uint) { - return double(_arg); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("runtime(uint256)", encodeArgs(u256(3))), encodeArgs(u256(6))); - ABI_CHECK(callContractFunction("initial()"), encodeArgs(u256(4))); -} - -BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime_equality_check) -{ - char const* sourceCode = R"( - contract C { - function (uint) internal returns (uint) x; - constructor() public { - x = double; - } - function test() public returns (bool) { - return x == double; - } - function double(uint _arg) public returns (uint _ret) { - _ret = _arg * 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(function_type_library_internal) -{ - char const* sourceCode = R"( - library Utils { - function reduce(uint[] memory array, function(uint, uint) internal returns (uint) f, uint init) internal returns (uint) { - for (uint i = 0; i < array.length; i++) { - init = f(array[i], init); - } - return init; - } - function sum(uint a, uint b) internal returns (uint) { - return a + b; - } - } - contract C { - function f(uint[] memory x) public returns (uint) { - return Utils.reduce(x, Utils.sum, 0); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256[])", 0x20, 3, u256(1), u256(7), u256(3)), encodeArgs(u256(11))); -} - - -BOOST_AUTO_TEST_CASE(call_function_returning_function) -{ - char const* sourceCode = R"( - contract test { - function f0() public returns (uint) { - return 2; - } - function f1() internal returns (function() internal returns (uint)) { - return f0; - } - function f2() internal returns (function() internal returns (function () internal returns (uint))) { - return f1; - } - function f3() internal returns (function() internal returns (function () internal returns (function () internal returns (uint)))) - { - return f2; - } - function f() public returns (uint) { - function() internal returns(function() internal returns(function() internal returns(function() internal returns(uint)))) x; - x = f3; - return x()()()(); - } - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "test"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(mapping_of_functions) -{ - char const* sourceCode = R"( - contract Flow { - bool public success; - - mapping (address => function () internal) stages; - - function stage0() internal { - stages[msg.sender] = stage1; - } - - function stage1() internal { - stages[msg.sender] = stage2; - } - - function stage2() internal { - success = true; - } - - constructor() public { - stages[msg.sender] = stage0; - } - - function f() public returns (uint) { - stages[msg.sender](); - return 7; - } - } - )"; - - compileAndRun(sourceCode, 0, "Flow"); - ABI_CHECK(callContractFunction("success()"), encodeArgs(false)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("success()"), encodeArgs(false)); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("success()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(packed_functions) -{ - char const* sourceCode = R"( - contract C { - // these should take the same slot - function() internal returns (uint) a; - function() external returns (uint) b; - function() external returns (uint) c; - function() internal returns (uint) d; - uint8 public x; - - function set() public { - x = 2; - d = g; - c = this.h; - b = this.h; - a = g; - } - function t1() public returns (uint) { - return a(); - } - function t2() public returns (uint) { - return b(); - } - function t3() public returns (uint) { - return a(); - } - function t4() public returns (uint) { - return b(); - } - function g() public returns (uint) { - return 7; - } - function h() public returns (uint) { - return 8; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("set()"), encodeArgs()); - ABI_CHECK(callContractFunction("t1()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("t2()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("t3()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("t4()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(function_memory_array) -{ - char const* sourceCode = R"( - contract C { - function a(uint x) public returns (uint) { return x + 1; } - function b(uint x) public returns (uint) { return x + 2; } - function c(uint x) public returns (uint) { return x + 3; } - function d(uint x) public returns (uint) { return x + 5; } - function e(uint x) public returns (uint) { return x + 8; } - function test(uint x, uint i) public returns (uint) { - function(uint) internal returns (uint)[] memory arr = - new function(uint) internal returns (uint)[](10); - arr[0] = a; - arr[1] = b; - arr[2] = c; - arr[3] = d; - arr[4] = e; - return arr[i](x); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(0)), encodeArgs(u256(11))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(1)), encodeArgs(u256(12))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(2)), encodeArgs(u256(13))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(3)), encodeArgs(u256(15))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(4)), encodeArgs(u256(18))); - ABI_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(5)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(function_delete_storage) -{ - char const* sourceCode = R"( - contract C { - function a() public returns (uint) { return 7; } - function() internal returns (uint) y; - function set() public returns (uint) { - y = a; - return y(); - } - function d() public returns (uint) { - delete y; - return 1; - } - function ca() public returns (uint) { - return y(); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("set()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("ca()"), encodeArgs(u256(7))); - ABI_CHECK(callContractFunction("d()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("ca()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(function_delete_stack) -{ - char const* sourceCode = R"( - contract C { - function a() public returns (uint) { return 7; } - function test() public returns (uint) { - function () returns (uint) y = a; - delete y; - y(); - } - } - )"; - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - ) -} - -BOOST_AUTO_TEST_CASE(copy_function_storage_array) -{ - char const* sourceCode = R"( - contract C { - function() internal returns (uint)[] x; - function() internal returns (uint)[] y; - function test() public returns (uint) { - x = new function() internal returns (uint)[](10); - x[9] = a; - y = x; - return y[9](); - } - function a() public returns (uint) { - return 7; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(7))); -} - -BOOST_AUTO_TEST_CASE(function_array_cross_calls) -{ - char const* sourceCode = R"( - contract D { - function f(function() external returns (function() external returns (uint))[] memory x) - public returns (function() external returns (uint)[3] memory r) - { - r[0] = x[0](); - r[1] = x[1](); - r[2] = x[2](); - } - } - contract C { - function test() public returns (uint, uint, uint) { - function() external returns (function() external returns (uint))[] memory x = - new function() external returns (function() external returns (uint))[](10); - for (uint i = 0; i < x.length; i ++) - x[i] = this.h; - x[0] = this.htwo; - function() external returns (uint)[3] memory y = (new D()).f(x); - return (y[0](), y[1](), y[2]()); - } - function e() public returns (uint) { return 5; } - function f() public returns (uint) { return 6; } - function g() public returns (uint) { return 7; } - uint counter; - function h() public returns (function() external returns (uint)) { - return counter++ == 0 ? this.f : this.g; - } - function htwo() public returns (function() external returns (uint)) { - return this.e; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(5), u256(6), u256(7))); -} - -BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage) -{ - char const* sourceCode = R"( - contract C { - function() internal returns (uint)[20] x; - int mutex; - function one() public returns (uint) { - function() internal returns (uint)[20] memory xmem; - x = xmem; - return 3; - } - function two() public returns (uint) { - if (mutex > 0) - return 7; - mutex = 1; - // If this test fails, it might re-execute this function. - x[0](); - return 2; - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("one()"), encodeArgs(u256(3))); - ABI_CHECK(callContractFunction("two()"), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(shift_constant_left) -{ - char const* sourceCode = R"( - contract C { - uint public a = 0x42 << 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(0x4200))); -} - -BOOST_AUTO_TEST_CASE(shift_negative_constant_left) -{ - char const* sourceCode = R"( - contract C { - int public a = -0x42 << 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(-0x4200))); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right) -{ - char const* sourceCode = R"( - contract C { - uint public a = 0x4200 >> 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(0x42))); -} - -BOOST_AUTO_TEST_CASE(shift_negative_constant_right) -{ - char const* sourceCode = R"( - contract C { - int public a = -0x4200 >> 8; - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(-0x42))); -} - -BOOST_AUTO_TEST_CASE(shift_left) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - return a << b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(240)), fromHex("4266000000000000000000000000000000000000000000000000000000000000")); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(256)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_uint32) -{ - char const* sourceCode = R"( - contract C { - function f(uint32 a, uint32 b) public returns (uint) { - return a << b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(32)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_uint8) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - return a << b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(0)), encodeArgs(u256(0x66))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(8)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_larger_type) -{ - // This basically tests proper cleanup and conversion. It should not convert x to int8. - char const* sourceCode = R"( - contract C { - function f() public returns (int8) { - uint8 x = 254; - int8 y = 1; - return y << x; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - a <<= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(240)), fromHex("4266000000000000000000000000000000000000000000000000000000000000")); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(256)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_left_assignment_different_type) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint8 b) public returns (uint) { - a <<= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(8)), encodeArgs(u256(0x426600))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(16)), encodeArgs(u256(0x42660000))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(17)), encodeArgs(u256(0x84cc0000))); - ABI_CHECK(callContractFunction("f(uint256,uint8)", u256(0x4266), u256(240)), fromHex("4266000000000000000000000000000000000000000000000000000000000000")); -} - -BOOST_AUTO_TEST_CASE(shift_right) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(1)<<255, u256(5)), encodeArgs(u256(1)<<250)); -} - -BOOST_AUTO_TEST_CASE(shift_right_garbled) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - assembly { - a := 0xffffffff - } - // Higher bits should be cleared before the shift - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(0xf))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(0xf))); -} - -BOOST_AUTO_TEST_CASE(shift_right_garbled_signed) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, uint8 b) public returns (int) { - assembly { - a := 0xfffffff0 - } - // Higher bits should be signextended before the shift - return a >> b; - } - function g(int8 a, uint8 b) public returns (int) { - assembly { - a := 0xf0 - } - // Higher bits should be signextended before the shift - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1003)), v2 ? encodeArgs() : encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1003)), v2 ? encodeArgs() : encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_uint32) -{ - char const* sourceCode = R"( - contract C { - function f(uint32 a, uint32 b) public returns (uint) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint32,uint32)", u256(0x4266), u256(17)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_uint8) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(0)), encodeArgs(u256(0x66))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x66), u256(8)), encodeArgs(u256(0x0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(uint a, uint b) public returns (uint) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_assignment_signed) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(0)), encodeArgs(u256(0x4266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_literal) -{ - char const* sourceCode = R"( - contract C { - function f1() public pure returns (bool) { - return (-4266 >> 0) == -4266; - } - function f2() public pure returns (bool) { - return (-4266 >> 1) == -2133; - } - function f3() public pure returns (bool) { - return (-4266 >> 4) == -267; - } - function f4() public pure returns (bool) { - return (-4266 >> 8) == -17; - } - function f5() public pure returns (bool) { - return (-4266 >> 16) == -1; - } - function f6() public pure returns (bool) { - return (-4266 >> 17) == -1; - } - function g1() public pure returns (bool) { - return (-4267 >> 0) == -4267; - } - function g2() public pure returns (bool) { - return (-4267 >> 1) == -2134; - } - function g3() public pure returns (bool) { - return (-4267 >> 4) == -267; - } - function g4() public pure returns (bool) { - return (-4267 >> 8) == -17; - } - function g5() public pure returns (bool) { - return (-4267 >> 16) == -1; - } - function g6() public pure returns (bool) { - return (-4267 >> 17) == -1; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f4()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f5()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("f6()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g1()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g2()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g3()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g4()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g5()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("g6()"), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int8) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, int8 b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(0)), encodeArgs(u256(-66))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(1)), encodeArgs(u256(-33))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(4)), encodeArgs(u256(-5))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(8)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(0)), encodeArgs(u256(-67))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(1)), encodeArgs(u256(-34))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(4)), encodeArgs(u256(-5))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(8)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int8) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, int8 b) public returns (int8) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int16) -{ - char const* sourceCode = R"( - contract C { - function f(int16 a, int16 b) public returns (int16) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int32) -{ - char const* sourceCode = R"( - contract C { - function f(int32 a, int32 b) public returns (int32) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int16) -{ - char const* sourceCode = R"( - contract C { - function f(int16 a, int16 b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int32) -{ - char const* sourceCode = R"( - contract C { - function f(int32 a, int32 b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-17))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_negative_rvalue) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - return a << b; - } - function g(int a, int b) public returns (int) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(1), u256(-1)), encodeArgs()); - ABI_CHECK(callContractFunction("g(int256,int256)", u256(1), u256(-1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(shift_negative_rvalue_assignment) -{ - char const* sourceCode = R"( - contract C { - function f(int a, int b) public returns (int) { - a <<= b; - return a; - } - function g(int a, int b) public returns (int) { - a >>= b; - return a; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(1), u256(-1)), encodeArgs()); - ABI_CHECK(callContractFunction("g(int256,int256)", u256(1), u256(-1)), encodeArgs()); -} - -BOOST_AUTO_TEST_CASE(shift_constant_left_assignment) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint a) { - a = 0x42; - a <<= 8; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x4200))); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right_assignment) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint a) { - a = 0x4200; - a >>= 8; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x42))); -} - -BOOST_AUTO_TEST_CASE(shift_cleanup) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint16 x) { - x = 0xffff; - x += 32; - x <<= 8; - x >>= 16; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x0))); -} - -BOOST_AUTO_TEST_CASE(shift_cleanup_garbled) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint8 x) { - assembly { - x := 0xffff - } - x >>= 8; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x0))); -} - -BOOST_AUTO_TEST_CASE(shift_overflow) -{ - char const* sourceCode = R"( - contract C { - function leftU(uint8 x, uint8 y) public returns (uint8) { - return x << y; - } - function leftS(int8 x, int8 y) public returns (int8) { - return x << y; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 8), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 1), encodeArgs(u256(254))); - ABI_CHECK(callContractFunction("leftU(uint8,uint8)", 255, 0), encodeArgs(u256(255))); - - // Result is -128 and output is sign-extended, not zero-padded. - ABI_CHECK(callContractFunction("leftS(int8,int8)", 1, 7), encodeArgs(u256(0) - 128)); - ABI_CHECK(callContractFunction("leftS(int8,int8)", 1, 6), encodeArgs(u256(64))); -} - BOOST_AUTO_TEST_CASE(shift_bytes) { char const* sourceCode = R"( @@ -11754,108 +5518,6 @@ BOOST_AUTO_TEST_CASE(shift_bytes_cleanup) ABI_CHECK(callContractFunction("right(uint8)", 8 * 8), encodeArgs(string(8, 0) + "123456789012")); } -BOOST_AUTO_TEST_CASE(exp_cleanup) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint8 x) { - uint8 y = uint8(2) ** uint8(8); - return 0 ** y; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); -} - -BOOST_AUTO_TEST_CASE(exp_cleanup_direct) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint8 x) { - return uint8(0) ** uint8(uint8(2) ** uint8(8)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); -} - -BOOST_AUTO_TEST_CASE(exp_cleanup_nonzero_base) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint8 x) { - return uint8(0x166) ** uint8(uint8(2) ** uint8(8)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1))); -} - -BOOST_AUTO_TEST_CASE(cleanup_in_compound_assign) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (uint, uint) { - uint32 a = 0xffffffff; - uint16 x = uint16(a); - uint16 y = x; - x /= 0x100; - y = y / 0x100; - return (x, y); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0xff), u256(0xff))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_in_modifiers) -{ - char const* sourceCode = R"( - contract C { - modifier m { - uint a = 1; - assembly { - a := 2 - } - if (a != 2) - revert(); - _; - } - function f() m public returns (bool) { - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(packed_storage_overflow) -{ - char const* sourceCode = R"( - contract C { - uint16 x = 0x1234; - uint16 a = 0xffff; - uint16 b; - function f() public returns (uint, uint, uint, uint) { - a++; - uint c = b; - delete b; - a -= 2; - return (x, c, b, a); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x1234), u256(0), u256(0), u256(0xfffe))); -} - BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) { char const* sourceCode = R"( @@ -11870,7 +5532,6 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) ) } - BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) { char const* sourceCode = R"( @@ -11900,97 +5561,6 @@ BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) ); } -BOOST_AUTO_TEST_CASE(recursive_structs) -{ - char const* sourceCode = R"( - contract C { - struct S { - S[] x; - } - S sstorage; - function f() public returns (uint) { - S memory s; - s.x = new S[](10); - delete s; - // TODO Uncomment after implemented. - // sstorage.x.push(); - delete sstorage; - return 1; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); -} - -BOOST_AUTO_TEST_CASE(invalid_instruction) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assembly { - invalid() - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ) -} - -BOOST_AUTO_TEST_CASE(assert_require) -{ - char const* sourceCode = R"( - contract C { - function f() public { - assert(false); - } - function g(bool val) public returns (bool) { - assert(val == true); - return true; - } - function h(bool val) public returns (bool) { - require(val); - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs()); - ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(true)); - ABI_CHECK(callContractFunction("h(bool)", false), encodeArgs()); - ABI_CHECK(callContractFunction("h(bool)", true), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(revert) -{ - char const* sourceCode = R"( - contract C { - uint public a = 42; - function f() public { - a = 1; - revert(); - } - function g() public { - a = 1; - assembly { - revert(0, 0) - } - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(42))); -} - BOOST_AUTO_TEST_CASE(revert_with_cause) { char const* sourceCode = R"( @@ -12249,85 +5819,6 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) } } -BOOST_AUTO_TEST_CASE(negative_stack_height) -{ - // This code was causing negative stack height during code generation - // because the stack height was not adjusted at the beginning of functions. - char const* sourceCode = R"( - contract C { - mapping(uint => Invoice) public invoices; - struct Invoice { - uint AID; - bool Aboola; - bool Aboolc; - bool exists; - } - function nredit(uint startindex) public pure returns(uint[500] memory CIDs, uint[500] memory dates, uint[500] memory RIDs, bool[500] memory Cboolas, uint[500] memory amounts){} - function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] memory AIDs, bool[500] memory Aboolas, uint[500] memory dates, bytes32[3][500] memory Abytesas, bytes32[3][500] memory bytesbs, bytes32[2][500] memory bytescs, uint[500] memory amounts, bool[500] memory Aboolbs, bool[500] memory Aboolcs){} - function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] memory BIDs, uint[500] memory dates, uint[500] memory RIDs, bool[500] memory Bboolas, bytes32[3][500] memory bytesbs,bytes32[2][500] memory bytescs, uint[500] memory amounts, bool[500] memory Bboolbs){} - } - )"; - compileAndRun(sourceCode, 0, "C"); -} - -BOOST_AUTO_TEST_CASE(literal_empty_string) -{ - char const* sourceCode = R"( - contract C { - bytes32 public x; - uint public a; - function f(bytes32 _x, uint _a) public { - x = _x; - a = _a; - } - function g() public { - this.f("", 2); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(2))); -} - -BOOST_AUTO_TEST_CASE(scientific_notation) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return 2e10 wei; - } - function g() public returns (uint) { - return 200e-2 wei; - } - function h() public returns (uint) { - return 2.5e1; - } - function i() public returns (int) { - return -2e10; - } - function j() public returns (int) { - return -200e-2; - } - function k() public returns (int) { - return -2.5e1; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(20000000000))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(2))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(25))); - ABI_CHECK(callContractFunction("i()"), encodeArgs(u256(-20000000000))); - ABI_CHECK(callContractFunction("j()"), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("k()"), encodeArgs(u256(-25))); - ) -} - BOOST_AUTO_TEST_CASE(interface_contract) { char const* sourceCode = R"( @@ -12363,70 +5854,6 @@ BOOST_AUTO_TEST_CASE(interface_contract) ABI_CHECK(callContractFunction("f(address)", recipient), encodeArgs(true)); } -BOOST_AUTO_TEST_CASE(keccak256_assembly) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (bytes32 ret) { - assembly { - ret := keccak256(0, 0) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ) -} - -BOOST_AUTO_TEST_CASE(multi_modifiers) -{ - // This triggered a bug in some version because the variable in the modifier was not - // unregistered correctly. - char const* sourceCode = R"( - contract C { - uint public x; - modifier m1 { - address a1 = msg.sender; - x++; - _; - } - function f1() m1() public { - x += 7; - } - function f2() m1() public { - x += 3; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f1()"), bytes()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f2()"), bytes()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(12))); -} - -BOOST_AUTO_TEST_CASE(inlineasm_empty_let) -{ - char const* sourceCode = R"( - contract C { - function f() public pure returns (uint a, uint b) { - assembly { - let x - let y, z - a := x - b := z - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0), u256(0))); - ) -} - BOOST_AUTO_TEST_CASE(bare_call_invalid_address) { char const* sourceCode = R"YY( @@ -12587,244 +6014,6 @@ BOOST_AUTO_TEST_CASE(bare_call_return_data) } } -BOOST_AUTO_TEST_CASE(delegatecall_return_value) -{ - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool, bytes memory) { - return address(this).delegatecall(abi.encodeWithSignature("get()")); - } - function assert0() external view { - assert(value == 0); - } - function assert0_delegated() external returns (bool, bytes memory) { - return address(this).delegatecall(abi.encodeWithSignature("assert0()")); - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 0x00)); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 1)); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 42)); - } - else - { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool) { - (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); - return success; - } - function assert0() external view { - assert(value == 0); - } - function assert0_delegated() external returns (bool) { - (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); - return success; - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - } -} - -BOOST_AUTO_TEST_CASE(function_types_sig) -{ - char const* sourceCode = R"( - contract C { - uint public x; - function f() public pure returns (bytes4) { - return this.f.selector; - } - function g() public returns (bytes4) { - function () pure external returns (bytes4) fun = this.f; - return fun.selector; - } - function h() public returns (bytes4) { - function () pure external returns (bytes4) fun = this.f; - return fun.selector; - } - function i() public pure returns (bytes4) { - return this.x.selector; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("i()"), encodeArgs(asString(FixedHash<4>(util::keccak256("x()")).asBytes()))); -} - -BOOST_AUTO_TEST_CASE(constant_string) -{ - char const* sourceCode = R"( - contract C { - bytes constant a = "\x03\x01\x02"; - bytes constant b = hex"030102"; - string constant c = "hello"; - function f() public returns (bytes memory) { - return a; - } - function g() public returns (bytes memory) { - return b; - } - function h() public returns (bytes memory) { - return bytes(c); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeDyn(string("\x03\x01\x02"))); - ABI_CHECK(callContractFunction("g()"), encodeDyn(string("\x03\x01\x02"))); - ABI_CHECK(callContractFunction("h()"), encodeDyn(string("hello"))); -} - -BOOST_AUTO_TEST_CASE(address_overload_resolution) -{ - char const* sourceCode = R"( - contract C { - function balance() public returns (uint) { - return 1; - } - function transfer(uint amount) public returns (uint) { - return amount; - } - } - contract D { - function f() public returns (uint) { - return (new C()).balance(); - } - function g() public returns (uint) { - return (new C()).transfer(5); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(5))); -} - -BOOST_AUTO_TEST_CASE(abi_encode) -{ - char const* sourceCode = R"( - contract C { - function f0() public returns (bytes memory) { - return abi.encode(); - } - function f1() public returns (bytes memory) { - return abi.encode(1, 2); - } - function f2() public returns (bytes memory) { - string memory x = "abc"; - return abi.encode(1, x, 2); - } - function f3() public returns (bytes memory r) { - // test that memory is properly allocated - string memory x = "abc"; - r = abi.encode(1, x, 2); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - function f4() public returns (bytes memory) { - bytes4 x = "abcd"; - return abi.encode(bytes2(x)); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x20, "ab")); -} - -BOOST_AUTO_TEST_CASE(abi_encode_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; uint[] b; } - function f0() public pure returns (bytes memory) { - return abi.encode(); - } - function f1() public pure returns (bytes memory) { - return abi.encode(1, 2); - } - function f2() public pure returns (bytes memory) { - string memory x = "abc"; - return abi.encode(1, x, 2); - } - function f3() public pure returns (bytes memory r) { - // test that memory is properly allocated - string memory x = "abc"; - r = abi.encode(1, x, 2); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - S s; - function f4() public returns (bytes memory r) { - string memory x = "abc"; - s.a = 7; - s.b.push(2); - s.b.push(3); - r = abi.encode(1, x, s, 2); - bytes memory y = "def"; - require(y[0] == "d"); - y[0] = "e"; - require(y[0] == "e"); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); - ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); - ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); - ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x160, 1, 0x80, 0xc0, 2, 3, "abc", 7, 0x40, 2, 2, 3)); -} - - BOOST_AUTO_TEST_CASE(abi_encodePacked) { char const* sourceCode = R"( @@ -12867,11 +6056,7 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked) ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02")); ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); - ABI_CHECK(callContractFunction("f4()"), encodeArgs( - 0x20, - 2 + 26 + 26 + 2, - "\x07\x01" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "\x12\x01" - )); + ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 2 + 26 + 26 + 2, "\x07\x01" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "\x12\x01")); ABI_CHECK(callContractFunction("f_literal()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); ABI_CHECK(callContractFunction("f_calldata()"), encodeArgs(0x20, 6, "\x01" "\xa5\xbf\xa1\xee" "\x02")); } @@ -12958,10 +6143,7 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_from_storage) ABI_CHECK(callContractFunction("lf()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); ABI_CHECK(callContractFunction("ld()"), encodeArgs(0x20, 5 * 32 + 2, "\x01" + asString(encodeArgs(payload)) + "\x02")); ABI_CHECK(callContractFunction("bytes_short()"), encodeArgs(0x20, 6, "\x01" "abcd\x02")); - ABI_CHECK( - callContractFunction("bytes_long()"), - encodeArgs(0x20, 42, "\x01" "0123456789012345678901234567890123456789\x02") - ); + ABI_CHECK(callContractFunction("bytes_long()"), encodeArgs(0x20, 42, "\x01" "0123456789012345678901234567890123456789\x02")); } } @@ -13196,7 +6378,6 @@ BOOST_AUTO_TEST_CASE(event_signature_in_library) BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E((uint8,int16),(uint8,int16))"))); } - BOOST_AUTO_TEST_CASE(abi_encode_with_selector) { char const* sourceCode = R"( @@ -13377,706 +6558,6 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) ABI_CHECK(callContractFunction("f4()"), expectation); } -BOOST_AUTO_TEST_CASE(abi_encode_empty_string) -{ - char const* sourceCode = R"( - // Tests that this will not end up using a "bytes0" type - // (which would assert) - contract C { - function f() public pure returns (bytes memory, bytes memory) { - return (abi.encode(""), abi.encodePacked("")); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - if (!solidity::test::CommonOptions::get().useABIEncoderV2) - { - // ABI Encoder V2 has slightly different padding, tested below. - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x40, 0xc0, - 0x60, 0x20, 0x00, 0x00, - 0x00 - )); - } -} - -BOOST_AUTO_TEST_CASE(abi_encode_empty_string_v2) -{ - char const* sourceCode = R"( - // Tests that this will not end up using a "bytes0" type - // (which would assert) - pragma experimental ABIEncoderV2; - contract C { - function f() public pure returns (bytes memory, bytes memory) { - return (abi.encode(""), abi.encodePacked("")); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x40, 0xa0, - 0x40, 0x20, 0x00, - 0x00 - )); -} - -BOOST_AUTO_TEST_CASE(abi_encode_rational) -{ - char const* sourceCode = R"( - // Tests that rational numbers (even negative ones) are encoded properly. - contract C { - function f() public pure returns (bytes memory) { - return abi.encode(1, -2); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x20, - 0x40, u256(1), u256(-2) - )); -} - -BOOST_AUTO_TEST_CASE(abi_encode_rational_v2) -{ - char const* sourceCode = R"( - // Tests that rational numbers (even negative ones) are encoded properly. - pragma experimental ABIEncoderV2; - contract C { - function f() public pure returns (bytes memory) { - return abi.encode(1, -2); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x20, - 0x40, u256(1), u256(-2) - )); -} - -BOOST_AUTO_TEST_CASE(abi_encode_call) -{ - char const* sourceCode = R"T( - contract C { - bool x; - function c(uint a, uint[] memory b) public { - require(a == 5); - require(b.length == 2); - require(b[0] == 6); - require(b[1] == 7); - x = true; - } - function f() public returns (bool) { - uint a = 5; - uint[] memory b = new uint[](2); - b[0] = 6; - b[1] = 7; - (bool success,) = address(this).call(abi.encodeWithSignature("c(uint256,uint256[])", a, b)); - require(success); - return x; - } - } - )T"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); -} - -BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) -{ - char const* sourceCode = R"( - contract C { - uint x; - function f() public returns (uint) { - x = 3; - return 1; - } - } - interface CView { - function f() view external returns (uint); - } - interface CPure { - function f() pure external returns (uint); - } - contract D { - function f() public returns (uint) { - return (new C()).f(); - } - function fview() public returns (uint) { - return (CView(address(new C()))).f(); - } - function fpure() public returns (uint) { - return (CPure(address(new C()))).f(); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - // This should work (called via CALL) - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - { - // These should throw (called via STATICCALL) - ABI_CHECK(callContractFunction("fview()"), encodeArgs()); - ABI_CHECK(callContractFunction("fpure()"), encodeArgs()); - } - else - { - ABI_CHECK(callContractFunction("fview()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1)); - } -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl(uint a, uint b) public returns (uint c) { - assembly { - c := shl(b, a) - } - } - function shr(uint a, uint b) public returns (uint c) { - assembly { - c := shr(b, a) - } - } - function sar(uint a, uint b) public returns (uint c) { - assembly { - c := sar(b, a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(4))); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"))); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - ) -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl_1() public returns (bool) { - uint c; - assembly { - c := shl(2, 1) - } - assert(c == 4); - return true; - } - function shl_2() public returns (bool) { - uint c; - assembly { - c := shl(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe); - return true; - } - function shl_3() public returns (bool) { - uint c; - assembly { - c := shl(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0); - return true; - } - function shr_1() public returns (bool) { - uint c; - assembly { - c := shr(1, 3) - } - assert(c == 1); - return true; - } - function shr_2() public returns (bool) { - uint c; - assembly { - c := shr(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - return true; - } - function shr_3() public returns (bool) { - uint c; - assembly { - c := shr(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0); - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("shl_1()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shl_2()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shl_3()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_1()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_2()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_3()") == encodeArgs(u256(1))); - ) -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople_combined) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl_zero(uint a) public returns (uint c) { - assembly { - c := shl(0, a) - } - } - function shr_zero(uint a) public returns (uint c) { - assembly { - c := shr(0, a) - } - } - function sar_zero(uint a) public returns (uint c) { - assembly { - c := sar(0, a) - } - } - - function shl_large(uint a) public returns (uint c) { - assembly { - c := shl(0x110, a) - } - } - function shr_large(uint a) public returns (uint c) { - assembly { - c := shr(0x110, a) - } - } - function sar_large(uint a) public returns (uint c) { - assembly { - c := sar(0x110, a) - } - } - - function shl_combined(uint a) public returns (uint c) { - assembly { - c := shl(4, shl(12, a)) - } - } - function shr_combined(uint a) public returns (uint c) { - assembly { - c := shr(4, shr(12, a)) - } - } - function sar_combined(uint a) public returns (uint c) { - assembly { - c := sar(4, sar(12, a)) - } - } - - function shl_combined_large(uint a) public returns (uint c) { - assembly { - c := shl(0xd0, shl(0x40, a)) - } - } - function shl_combined_overflow(uint a) public returns (uint c) { - assembly { - c := shl(0x01, shl(not(0x00), a)) - } - } - function shr_combined_large(uint a) public returns (uint c) { - assembly { - c := shr(0xd0, shr(0x40, a)) - } - } - function shr_combined_overflow(uint a) public returns (uint c) { - assembly { - c := shr(0x01, shr(not(0x00), a)) - } - } - function sar_combined_large(uint a) public returns (uint c) { - assembly { - c := sar(0xd0, sar(0x40, a)) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256("0xffff")) == encodeArgs(u256("0xffff"))); - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("shr_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256("0xffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256("0xffff")) == encodeArgs(u256("0xffff0000"))); - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000"))); - BOOST_CHECK(callContractFunction("shr_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0x00007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256("0xffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_overflow(uint256)", u256(2)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_overflow(uint256)", u256(2)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - ) -} - -BOOST_AUTO_TEST_CASE(senders_balance) -{ - char const* sourceCode = R"( - contract C { - function f() public view returns (uint) { - return msg.sender.balance; - } - } - contract D { - C c = new C(); - constructor() public payable { } - function f() public view returns (uint) { - return c.f(); - } - } - )"; - compileAndRun(sourceCode, 27, "D"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27))); -} - -BOOST_AUTO_TEST_CASE(abi_decode_trivial) -{ - char const* sourceCode = R"( - contract C { - function f(bytes memory data) public pure returns (uint) { - return abi.decode(data, (uint)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20, 33), encodeArgs(u256(33))); -} - -BOOST_AUTO_TEST_CASE(abi_encode_decode_simple) -{ - char const* sourceCode = R"XX( - contract C { - function f() public pure returns (uint, bytes memory) { - bytes memory arg = "abcdefg"; - return abi.decode(abi.encode(uint(33), arg), (uint, bytes)); - } - } - )XX"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f()"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_simple) -{ - char const* sourceCode = R"( - contract C { - function f(bytes memory data) public pure returns (uint, bytes memory) { - return abi.decode(data, (uint, bytes)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; uint[] b; } - function f() public pure returns (S memory) { - S memory s; - s.a = 8; - s.b = new uint[](3); - s.b[0] = 9; - s.b[1] = 10; - s.b[2] = 11; - return abi.decode(abi.encode(s), (S)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f()"), - encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11) - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_simple_storage) -{ - char const* sourceCode = R"( - contract C { - bytes data; - function f(bytes memory _data) public returns (uint, bytes memory) { - data = _data; - return abi.decode(data, (uint, bytes)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_v2_storage) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - bytes data; - struct S { uint a; uint[] b; } - function f() public returns (S memory) { - S memory s; - s.a = 8; - s.b = new uint[](3); - s.b[0] = 9; - s.b[1] = 10; - s.b[2] = 11; - data = abi.encode(s); - return abi.decode(data, (S)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f()"), - encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11) - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_calldata) -{ - char const* sourceCode = R"( - contract C { - function f(bytes calldata data) external pure returns (uint, bytes memory r) { - return abi.decode(data, (uint, bytes)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), - encodeArgs(33, 0x40, 7, "abcdefg") - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_v2_calldata) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; uint[] b; } - function f(bytes calldata data) external pure returns (S memory) { - return abi.decode(data, (S)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 0x20 * 7, 0x20, 33, 0x40, 3, 10, 11, 12), - encodeArgs(0x20, 33, 0x40, 3, 10, 11, 12) - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_static_array) -{ - char const* sourceCode = R"( - contract C { - function f(bytes calldata data) external pure returns (uint[2][3] memory) { - return abi.decode(data, (uint[2][3])); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6), - encodeArgs(1, 2, 3, 4, 5, 6) - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_static_array_v2) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes calldata data) external pure returns (uint[2][3] memory) { - return abi.decode(data, (uint[2][3])); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6), - encodeArgs(1, 2, 3, 4, 5, 6) - ); -} - -BOOST_AUTO_TEST_CASE(abi_decode_dynamic_array) -{ - char const* sourceCode = R"( - contract C { - function f(bytes calldata data) external pure returns (uint[] memory) { - return abi.decode(data, (uint[])); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(bytes)", 0x20, 6 * 0x20, 0x20, 4, 3, 4, 5, 6), - encodeArgs(0x20, 4, 3, 4, 5, 6) - ); -} - -BOOST_AUTO_TEST_CASE(write_storage_external) -{ - char const* sourceCode = R"( - contract C { - uint public x; - function f(uint y) public payable { - x = y; - } - function g(uint y) external { - x = y; - } - function h() public { - this.g(12); - } - } - contract D { - C c = new C(); - function f() public payable returns (uint) { - c.g(3); - return c.x(); - } - function g() public returns (uint) { - c.g(8); - return c.x(); - } - function h() public returns (uint) { - c.h(); - return c.x(); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(8)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(12)); -} - -BOOST_AUTO_TEST_CASE(test_underscore_in_hex) -{ - char const* sourceCode = R"( - contract test { - function f(bool cond) public pure returns (uint) { - uint32 x = 0x1234_ab; - uint y = 0x1234_abcd_1234; - return cond ? x : y; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(u256(0x1234ab))); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(0x1234abcd1234))); -} - -BOOST_AUTO_TEST_CASE(flipping_sign_tests) -{ - char const* sourceCode = R"( - contract test { - function f() public returns (bool){ - int x = -2**255; - assert(-x == x); - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); -} -BOOST_AUTO_TEST_CASE(external_public_override) -{ - char const* sourceCode = R"( - contract A { - function f() external virtual returns (uint) { return 1; } - } - contract B is A { - function f() public override returns (uint) { return 2; } - function g() public returns (uint) { return f(); } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ) -} - -BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables) -{ - char const* sourceCode = R"( - contract C { - function () internal returns (uint) x; - function set() public { - C.x = g; - } - function g() public pure returns (uint) { return 2; } - function h() public returns (uint) { return C.x(); } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); - ABI_CHECK(callContractFunction("h()"), encodeArgs()); - ABI_CHECK(callContractFunction("set()"), encodeArgs()); - ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); -} - BOOST_AUTO_TEST_CASE(code_access) { char const* sourceCode = R"( @@ -14118,91 +6599,6 @@ BOOST_AUTO_TEST_CASE(code_access) ABI_CHECK(codeRuntime1, codeRuntime2); } -BOOST_AUTO_TEST_CASE(code_access_padding) -{ - char const* sourceCode = R"( - contract C { - function diff() public pure returns (uint remainder) { - bytes memory a = type(D).creationCode; - bytes memory b = type(D).runtimeCode; - assembly { remainder := mod(sub(b, a), 0x20) } - } - } - contract D { - function f() public pure returns (uint) { return 7; } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // This checks that the allocation function pads to multiples of 32 bytes - ABI_CHECK(callContractFunction("diff()"), encodeArgs(0)); -} - -BOOST_AUTO_TEST_CASE(code_access_create) -{ - char const* sourceCode = R"( - contract C { - function test() public returns (uint) { - bytes memory c = type(D).creationCode; - D d; - assembly { - d := create(0, add(c, 0x20), mload(c)) - } - return d.f(); - } - } - contract D { - uint x; - constructor() public { x = 7; } - function f() public view returns (uint) { return x; } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(7)); -} - -BOOST_AUTO_TEST_CASE(code_access_content) -{ - char const* sourceCode = R"( - contract C { - function testRuntime() public returns (bool) { - D d = new D(); - bytes32 runtimeHash = keccak256(type(D).runtimeCode); - bytes32 otherHash; - uint size; - assembly { - size := extcodesize(d) - extcodecopy(d, mload(0x40), 0, size) - otherHash := keccak256(mload(0x40), size) - } - require(size == type(D).runtimeCode.length); - require(runtimeHash == otherHash); - return true; - } - function testCreation() public returns (bool) { - D d = new D(); - bytes32 creationHash = keccak256(type(D).creationCode); - require(creationHash == d.x()); - return true; - } - } - contract D { - bytes32 public x; - constructor() public { - bytes32 codeHash; - assembly { - let size := codesize() - codecopy(mload(0x40), 0, size) - codeHash := keccak256(mload(0x40), size) - } - x = codeHash; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true)); - ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(contract_name) { char const* sourceCode = R"( @@ -14295,22 +6691,6 @@ BOOST_AUTO_TEST_CASE(uninitialized_internal_storage_function) BOOST_CHECK(result != encodeArgs(0)); } -BOOST_AUTO_TEST_CASE(uninitialized_internal_storage_function_call) -{ - char const* sourceCode = R"( - contract Test { - function() internal x; - function f() public returns (uint r) { - x(); - return 2; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - ABI_CHECK(callContractFunction("f()"), bytes{}); -} - BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index dcfb3e6bf..0afe95c6d 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -60,6 +61,7 @@ bytes SolidityExecutionFramework::compileContract( formatter.printErrorInformation(*error); BOOST_ERROR("Compiling contract failed"); } + std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName); evmasm::LinkerObject obj; if (m_compileViaYul) { @@ -70,9 +72,7 @@ bytes SolidityExecutionFramework::compileContract( // get code that does not exhaust the stack. OptimiserSettings::full() ); - if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized( - _contractName.empty() ? m_compiler.lastContractName() : _contractName - ))) + if (!asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName))) { langutil::SourceReferenceFormatter formatter(std::cerr); @@ -84,7 +84,9 @@ bytes SolidityExecutionFramework::compileContract( obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); } else - obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); + obj = m_compiler.object(contractName); BOOST_REQUIRE(obj.linkReferences.empty()); + if (m_showMetadata) + cout << "metadata: " << m_compiler.metadata(contractName) << endl; return obj.bytecode; } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index dfcf4a46b..aaa17525f 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -41,9 +41,9 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework { public: - SolidityExecutionFramework() {} + SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion): - ExecutionFramework(_evmVersion) + ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} virtual bytes const& compileAndRunWithoutCheck( @@ -68,6 +68,7 @@ public: protected: solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; + bool m_showMetadata = false; RevertStrings m_revertStrings = RevertStrings::Default; }; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index cb0066bee..cdaee104b 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -119,13 +119,9 @@ bytes compileFirstExpression( NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); - vector inheritanceHierarchy; for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*contract), "Resolving names failed"); - inheritanceHierarchy = vector(1, contract); - } for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -144,7 +140,7 @@ bytes compileFirstExpression( RevertStrings::Default ); context.resetVisitedNodes(contract); - context.setInheritanceHierarchy(inheritanceHierarchy); + context.setMostDerivedContract(*contract); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack context.adjustStackOffset(parametersSize); for (vector const& variable: _localVariables) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 1938cfe86..3ec506998 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -37,20 +37,7 @@ namespace fs = boost::filesystem; SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion, bool _parserErrorRecovery): CommonSyntaxTest(_filename, _evmVersion) { - if (m_settings.count("optimize-yul")) - { - if (m_settings["optimize-yul"] == "true") - { - m_validatedSettings["optimize-yul"] = "true"; - m_settings.erase("optimize-yul"); - } - else if (m_settings["optimize-yul"] == "false") - { - m_validatedSettings["optimize-yul"] = "false"; - m_settings.erase("optimize-yul"); - m_optimiseYul = false; - } - } + m_optimiseYul = m_reader.boolSetting("optimize-yul", true); m_parserErrorRecovery = _parserErrorRecovery; } @@ -60,7 +47,7 @@ TestCase::TestResult SyntaxTest::run(ostream& _stream, string const& _linePrefix parseAndAnalyze(); filterObtainedErrors(); - return printExpectationAndError(_stream, _linePrefix, _formatted) ? TestResult::Success : TestResult::Failure; + return conclude(_stream, _linePrefix, _formatted); } void SyntaxTest::setupCompiler() diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index f0892b83b..1fd96ff76 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1120000 -// executionCost: 1160 -// totalCost: 1121160 +// codeDepositCost: 1094400 +// executionCost: 1134 +// totalCost: 1095534 // external: // a(): 1130 // b(uint256): infinite diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_dynamic_array.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_dynamic_array.sol new file mode 100644 index 000000000..180d89ec1 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_dynamic_array.sol @@ -0,0 +1,8 @@ +contract C { + function f(bytes calldata data) external pure returns (uint256[] memory) { + return abi.decode(data, (uint256[])); + } +} + +// ---- +// f(bytes): 0x20, 0xc0, 0x20, 0x4, 0x3, 0x4, 0x5, 0x6 -> 0x20, 0x4, 0x3, 0x4, 0x5, 0x6 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array.sol new file mode 100644 index 000000000..270a5abc0 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array.sol @@ -0,0 +1,12 @@ +contract C { + function f(bytes calldata data) + external + pure + returns (uint256[2][3] memory) + { + return abi.decode(data, (uint256[2][3])); + } +} + +// ---- +// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol new file mode 100644 index 000000000..46450f704 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bytes calldata data) + external + pure + returns (uint256[2][3] memory) + { + return abi.decode(data, (uint256[2][3])); + } +} + +// ---- +// f(bytes): 0x20, 0xc0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 -> 1, 2, 3, 4, 5, 6 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_trivial.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_trivial.sol new file mode 100644 index 000000000..7b873951f --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_trivial.sol @@ -0,0 +1,8 @@ +contract C { + function f(bytes memory data) public pure returns (uint256) { + return abi.decode(data, (uint256)); + } +} + +// ---- +// f(bytes): 0x20, 0x20, 0x21 -> 33 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol new file mode 100644 index 000000000..1dbf320bd --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[] b; + } + + function f() public pure returns (S memory) { + S memory s; + s.a = 8; + s.b = new uint256[](3); + s.b[0] = 9; + s.b[1] = 10; + s.b[2] = 11; + return abi.decode(abi.encode(s), (S)); + } +} + +// ---- +// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol new file mode 100644 index 000000000..a5e7a9af8 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[] b; + } + + function f(bytes calldata data) external pure returns (S memory) { + return abi.decode(data, (S)); + } +} + +// ---- +// f(bytes): 0x20, 0xe0, 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc -> 0x20, 0x21, 0x40, 0x3, 0xa, 0xb, 0xc diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol new file mode 100644 index 000000000..95b667e47 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + bytes data; + struct S { + uint256 a; + uint256[] b; + } + + function f() public returns (S memory) { + S memory s; + s.a = 8; + s.b = new uint256[](3); + s.b[0] = 9; + s.b[1] = 10; + s.b[2] = 11; + data = abi.encode(s); + return abi.decode(data, (S)); + } +} + +// ---- +// f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol new file mode 100644 index 000000000..ac17cf80e --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol @@ -0,0 +1,36 @@ +contract C { + function f0() public returns (bytes memory) { + return abi.encode(); + } + + function f1() public returns (bytes memory) { + return abi.encode(1, 2); + } + + function f2() public returns (bytes memory) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + + function f3() public returns (bytes memory r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + + function f4() public returns (bytes memory) { + bytes4 x = "abcd"; + return abi.encode(bytes2(x)); + } +} + +// ---- +// f0() -> 0x20, 0x0 +// f1() -> 0x20, 0x40, 0x1, 0x2 +// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f4() -> 0x20, 0x20, "ab" diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol new file mode 100644 index 000000000..d73db5da6 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_call.sol @@ -0,0 +1,26 @@ +contract C { + bool x; + + function c(uint256 a, uint256[] memory b) public { + require(a == 5); + require(b.length == 2); + require(b[0] == 6); + require(b[1] == 7); + x = true; + } + + function f() public returns (bool) { + uint256 a = 5; + uint256[] memory b = new uint256[](2); + b[0] = 6; + b[1] = 7; + (bool success, ) = address(this).call( + abi.encodeWithSignature("c(uint256,uint256[])", a, b) + ); + require(success); + return x; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol new file mode 100644 index 000000000..1fbcfa53d --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (uint256, bytes memory) { + bytes memory arg = "abcdefg"; + return abi.decode(abi.encode(uint256(33), arg), (uint256, bytes)); + } +} + +// ---- +// f() -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol new file mode 100644 index 000000000..704fd54dc --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol @@ -0,0 +1,9 @@ +// Tests that rational numbers (even negative ones) are encoded properly. +contract C { + function f() public pure returns (bytes memory) { + return abi.encode(1, -2); + } +} + +// ---- +// f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiDecodeV1/decode_slice.sol b/test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol similarity index 100% rename from test/libsolidity/semanticTests/abiDecodeV1/decode_slice.sol rename to test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol diff --git a/test/libsolidity/semanticTests/abiDecodeV1/dynamic_memory_copy.sol b/test/libsolidity/semanticTests/abiEncoderV1/dynamic_memory_copy.sol similarity index 100% rename from test/libsolidity/semanticTests/abiDecodeV1/dynamic_memory_copy.sol rename to test/libsolidity/semanticTests/abiEncoderV1/dynamic_memory_copy.sol diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol new file mode 100644 index 000000000..373334ee7 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol @@ -0,0 +1,13 @@ +// Tests that this will not end up using a "bytes0" type +// (which would assert) +pragma experimental ABIEncoderV2; + + +contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } +} + +// ---- +// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol new file mode 100644 index 000000000..55047880a --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol @@ -0,0 +1,12 @@ +// Tests that rational numbers (even negative ones) are encoded properly. +pragma experimental ABIEncoderV2; + + +contract C { + function f() public pure returns (bytes memory) { + return abi.encode(1, -2); + } +} + +// ---- +// f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol new file mode 100644 index 000000000..f6510b53d --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol @@ -0,0 +1,53 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[] b; + } + + function f0() public pure returns (bytes memory) { + return abi.encode(); + } + + function f1() public pure returns (bytes memory) { + return abi.encode(1, 2); + } + + function f2() public pure returns (bytes memory) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + + function f3() public pure returns (bytes memory r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + + S s; + + function f4() public returns (bytes memory r) { + string memory x = "abc"; + s.a = 7; + s.b.push(2); + s.b.push(3); + r = abi.encode(1, x, s, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } +} + +// ---- +// f0() -> 0x20, 0x0 +// f1() -> 0x20, 0x40, 0x1, 0x2 +// f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" +// f4() -> 0x20, 0x160, 0x1, 0x80, 0xc0, 0x2, 0x3, "abc", 0x7, 0x40, 0x2, 0x2, 0x3 diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol new file mode 100644 index 000000000..6b6e55e02 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol @@ -0,0 +1,11 @@ +contract C { + function f(bytes calldata data) + external + pure + returns (uint256, bytes memory r) + { + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol new file mode 100644 index 000000000..9ae6602f3 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol @@ -0,0 +1,7 @@ +contract C { + function f(bytes memory data) public pure returns (uint256, bytes memory) { + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol new file mode 100644 index 000000000..af37480a3 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol @@ -0,0 +1,10 @@ +contract C { + bytes data; + + function f(bytes memory _data) public returns (uint256, bytes memory) { + data = _data; + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol new file mode 100644 index 000000000..6ca284908 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol @@ -0,0 +1,11 @@ +// Tests that this will not end up using a "bytes0" type +// (which would assert) +contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f() -> 0x40, 0xc0, 0x60, 0x20, 0x0, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol new file mode 100644 index 000000000..a9402aa86 --- /dev/null +++ b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol @@ -0,0 +1,6 @@ +contract Lotto { + uint256 public constant ticketPrice = 555; +} + +// ---- +// ticketPrice() -> 555 diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol new file mode 100644 index 000000000..e2b59b530 --- /dev/null +++ b/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol @@ -0,0 +1,8 @@ +contract Lotto { + uint256 public ticketPrice = 500; +} + +// ==== +// compileViaYul: also +// ---- +// ticketPrice() -> 500 diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol new file mode 100644 index 000000000..d1e9f5c20 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol @@ -0,0 +1,12 @@ +contract C { + function test() public returns (uint256) { + // Note that this only works because computation on literals is done using + // unbounded integers. + if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 1; + if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) return 2; + return 0; + } +} + +// ---- +// test() -> 0 diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol new file mode 100644 index 000000000..7585980e1 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol @@ -0,0 +1,24 @@ +contract C { + function f(uint256 d) public pure returns (uint256) { + addmod(1, 2, d); + return 2; + } + + function g(uint256 d) public pure returns (uint256) { + mulmod(1, 2, d); + return 2; + } + + function h() public pure returns (uint256) { + mulmod(0, 1, 2); + mulmod(1, 0, 2); + addmod(0, 1, 2); + addmod(1, 0, 2); + return 2; + } +} + +// ---- +// f(uint256): 0 -> FAILURE +// g(uint256): 0 -> FAILURE +// h() -> 2 diff --git a/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol new file mode 100644 index 000000000..127320fc6 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol @@ -0,0 +1,15 @@ +contract C { + function div(uint256 a, uint256 b) public returns (uint256) { + return a / b; + } + + function mod(uint256 a, uint256 b) public returns (uint256) { + return a % b; + } +} + +// ---- +// div(uint256,uint256): 7, 2 -> 3 +// div(uint256,uint256): 7, 0 -> FAILURE # throws # +// mod(uint256,uint256): 7, 2 -> 1 +// mod(uint256,uint256): 7, 0 -> FAILURE # throws # diff --git a/test/libsolidity/semanticTests/array/array_copy_different_packing.sol b/test/libsolidity/semanticTests/array/array_copy_different_packing.sol new file mode 100644 index 000000000..f0afcffc9 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_different_packing.sol @@ -0,0 +1,21 @@ +contract c { + bytes8[] data1; // 4 per slot + bytes10[] data2; // 3 per slot + + function test() + public + returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) + { + data1 = new bytes8[](9); + for (uint256 i = 0; i < data1.length; ++i) data1[i] = bytes8(uint64(i)); + data2 = data1; + a = data2[1]; + b = data2[2]; + c = data2[3]; + d = data2[4]; + e = data2[5]; + } +} + +// ---- +// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/array_copy_nested_array.sol b/test/libsolidity/semanticTests/array/array_copy_nested_array.sol new file mode 100644 index 000000000..9330d3162 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_nested_array.sol @@ -0,0 +1,15 @@ +contract c { + uint256[4][] a; + uint256[10][] b; + uint256[][] c; + + function test(uint256[2][] calldata d) external returns (uint256) { + a = d; + b = a; + c = b; + return c[1][1] | c[1][2] | c[1][3] | c[1][4]; + } +} + +// ---- +// test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol b/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol new file mode 100644 index 000000000..0e225f1e5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol @@ -0,0 +1,20 @@ +// NOTE: This does not really test copying from storage to ABI directly, +// because it will always copy to memory first. +contract c { + int16[] x; + + function test() public returns (int16[] memory) { + x.push(int16(-1)); + x.push(int16(-1)); + x.push(int16(8)); + x.push(int16(-16)); + x.push(int16(-2)); + x.push(int16(6)); + x.push(int16(8)); + x.push(int16(-1)); + return x; + } +} + +// ---- +// test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol new file mode 100644 index 000000000..4432776fb --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol @@ -0,0 +1,14 @@ +contract c { + uint256[9] data1; + uint256[] data2; + + function test() public returns (uint256 x, uint256 y) { + data1[8] = 4; + data2 = data1; + x = data2.length; + y = data2[8]; + } +} + +// ---- +// test() -> 9, 4 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol new file mode 100644 index 000000000..71601d477 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol @@ -0,0 +1,17 @@ +contract c { + uint256[40] data1; + uint256[20] data2; + + function test() public returns (uint256 x, uint256 y) { + data1[30] = 4; + data1[2] = 7; + data1[3] = 9; + data2[3] = 8; + data1 = data2; + x = data1[3]; + y = data1[30]; // should be cleared + } +} + +// ---- +// test() -> 8, 0 diff --git a/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol b/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol new file mode 100644 index 000000000..1bbd95f6f --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol @@ -0,0 +1,22 @@ +// since the copy always copies whole slots, we have to make sure that the source size maxes +// out a whole slot and at the same time there are still elements left in the target at that point +contract c { + bytes8[4] data1; // fits into one slot + bytes10[6] data2; // 4 elements need two slots + + function test() public returns (bytes10 r1, bytes10 r2, bytes10 r3) { + data1[0] = bytes8(uint64(1)); + data1[1] = bytes8(uint64(2)); + data1[2] = bytes8(uint64(3)); + data1[3] = bytes8(uint64(4)); + for (uint256 i = 0; i < data2.length; ++i) + data2[i] = bytes10(uint80(0xffff00 | (1 + i))); + data2 = data1; + r1 = data2[3]; + r2 = data2[4]; + r3 = data2[5]; + } +} + +// ---- +// test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/array/array_copy_target_simple.sol b/test/libsolidity/semanticTests/array/array_copy_target_simple.sol new file mode 100644 index 000000000..ab82589c2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_copy_target_simple.sol @@ -0,0 +1,21 @@ +contract c { + bytes8[9] data1; // 4 per slot + bytes17[10] data2; // 1 per slot, no offset counter + + function test() + public + returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) + { + for (uint256 i = 0; i < data1.length; ++i) data1[i] = bytes8(uint64(i)); + data2[8] = data2[9] = bytes8(uint64(2)); + data2 = data1; + a = data2[1]; + b = data2[2]; + c = data2[3]; + d = data2[4]; + e = data2[9]; + } +} + +// ---- +// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0 diff --git a/test/libsolidity/semanticTests/array/array_pop.sol b/test/libsolidity/semanticTests/array/array_pop.sol new file mode 100644 index 000000000..5667b61d4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop.sol @@ -0,0 +1,16 @@ +contract c { + uint256[] data; + + function test() public returns (uint256 x, uint256 l) { + data.push(7); + data.push(3); + x = data.length; + data.pop(); + x = data.length; + data.pop(); + l = data.length; + } +} + +// ---- +// test() -> 1, 0 diff --git a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol new file mode 100644 index 000000000..8fc018d72 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol @@ -0,0 +1,11 @@ +contract c { + uint256[] data; + + function test() public returns (bool) { + data.pop(); + return true; + } +} + +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/array_pop_isolated.sol b/test/libsolidity/semanticTests/array/array_pop_isolated.sol new file mode 100644 index 000000000..2e6eac83e --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_pop_isolated.sol @@ -0,0 +1,13 @@ +// This tests that the compiler knows the correct size of the function on the stack. +contract c { + uint256[] data; + + function test() public returns (uint256 x) { + x = 2; + data.pop; + x = 3; + } +} + +// ---- +// test() -> 3 diff --git a/test/libsolidity/semanticTests/array/array_push.sol b/test/libsolidity/semanticTests/array/array_push.sol new file mode 100644 index 000000000..bd8200a37 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_push.sol @@ -0,0 +1,19 @@ +contract c { + uint256[] data; + + function test() + public + returns (uint256 x, uint256 y, uint256 z, uint256 l) + { + data.push(5); + x = data[0]; + data.push(4); + y = data[1]; + data.push(3); + l = data.length; + z = data[2]; + } +} + +// ---- +// test() -> 5, 4, 3, 3 diff --git a/test/libsolidity/semanticTests/array/array_push_packed_array.sol b/test/libsolidity/semanticTests/array/array_push_packed_array.sol new file mode 100644 index 000000000..dd5e2e855 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_push_packed_array.sol @@ -0,0 +1,16 @@ +contract c { + uint80[] x; + + function test() public returns (uint80, uint80, uint80, uint80) { + x.push(1); + x.push(2); + x.push(3); + x.push(4); + x.push(5); + x.pop(); + return (x[0], x[1], x[2], x[3]); + } +} + +// ---- +// test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/array_push_struct.sol b/test/libsolidity/semanticTests/array/array_push_struct.sol new file mode 100644 index 000000000..e407fc025 --- /dev/null +++ b/test/libsolidity/semanticTests/array/array_push_struct.sol @@ -0,0 +1,23 @@ +contract c { + struct S { + uint16 a; + uint16 b; + uint16[3] c; + uint16[] d; + } + S[] data; + + function test() public returns (uint16, uint16, uint16, uint16) { + S memory s; + s.a = 2; + s.b = 3; + s.c[2] = 4; + s.d = new uint16[](4); + s.d[2] = 5; + data.push(s); + return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]); + } +} + +// ---- +// test() -> 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol new file mode 100644 index 000000000..9cec148c7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol @@ -0,0 +1,18 @@ +contract Test { + uint24[3][] public data; + + function set(uint24[3][] memory _data) public returns (uint256) { + data = _data; + return data.length; + } + + function get() public returns (uint24[3][] memory) { + return data; + } +} +// ---- +// set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06 +// data(uint256,uint256): 0x02, 0x02 -> 0x09 +// data(uint256,uint256): 0x05, 0x01 -> 0x11 +// data(uint256,uint256): 0x06, 0x00 -> FAILURE +// get() -> 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol b/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol new file mode 100644 index 000000000..d21bcc53f --- /dev/null +++ b/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol @@ -0,0 +1,13 @@ +contract Test { + function set(uint24[3][] memory _data, uint256 a, uint256 b) + public + returns (uint256 l, uint256 e) + { + l = _data.length; + e = _data[a][b]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(uint24[3][],uint256,uint256): 0x60, 0x03, 0x02, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06, 0x0c diff --git a/test/libsolidity/semanticTests/array/byte_array_pop.sol b/test/libsolidity/semanticTests/array/byte_array_pop.sol new file mode 100644 index 000000000..5ed849702 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop.sol @@ -0,0 +1,17 @@ +contract c { + bytes data; + + function test() public returns (uint256 x, uint256 y, uint256 l) { + data.push(0x07); + data.push(0x03); + x = data.length; + data.pop(); + data.pop(); + data.push(0x02); + y = data.length; + l = data.length; + } +} + +// ---- +// test() -> 2, 1, 1 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol new file mode 100644 index 000000000..2589f1f55 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function test() public returns (bytes memory) { + for (uint256 i = 0; i < 33; i++) data.push(0x03); + for (uint256 j = 0; j < 4; j++) data.pop(); + return data; + } +} + +// ---- +// test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol new file mode 100644 index 000000000..30ffa3a4c --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol @@ -0,0 +1,14 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + + function test() public returns (bool) { + data.pop(); + return true; + } +} + +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol b/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol new file mode 100644 index 000000000..a9e3fd383 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol @@ -0,0 +1,13 @@ +// This tests that the compiler knows the correct size of the function on the stack. +contract c { + bytes data; + + function test() public returns (uint256 x) { + x = 2; + data.pop; + x = 3; + } +} + +// ---- +// test() -> 3 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol new file mode 100644 index 000000000..1f6d500bd --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function test() public returns (bytes memory) { + for (uint256 i = 0; i < 34; i++) data.push(0x03); + data.pop(); + return data; + } +} + +// ---- +// test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/byte_array_push.sol b/test/libsolidity/semanticTests/array/byte_array_push.sol new file mode 100644 index 000000000..67ed87b69 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_push.sol @@ -0,0 +1,18 @@ +contract c { + bytes data; + + function test() public returns (bool x) { + data.push(0x05); + if (data.length != 1) return true; + if (data[0] != 0x05) return true; + data.push(0x04); + if (data[1] != 0x04) return true; + data.push(0x03); + uint256 l = data.length; + if (data[2] != 0x03) return true; + if (l != 0x03) return true; + } +} + +// ---- +// test() -> false diff --git a/test/libsolidity/semanticTests/array/byte_array_push_transition.sol b/test/libsolidity/semanticTests/array/byte_array_push_transition.sol new file mode 100644 index 000000000..ceecf6726 --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_push_transition.sol @@ -0,0 +1,18 @@ +// Tests transition between short and long encoding +contract c { + bytes data; + + function test() public returns (uint256) { + for (uint8 i = 1; i < 40; i++) { + data.push(bytes1(i)); + if (data.length != i) return 0x1000 + i; + if (data[data.length - 1] != bytes1(i)) return i; + } + for (uint8 i = 1; i < 40; i++) + if (data[i - 1] != bytes1(i)) return 0x1000000 + i; + return 0; + } +} + +// ---- +// test() -> 0 diff --git a/test/libsolidity/semanticTests/array/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/bytes_delete_element.sol new file mode 100644 index 000000000..e3a8ec19d --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_delete_element.sol @@ -0,0 +1,19 @@ +contract c { + bytes data; + + function test1() external returns (bool) { + data = new bytes(100); + for (uint256 i = 0; i < data.length; i++) data[i] = bytes1(uint8(i)); + delete data[94]; + delete data[96]; + delete data[98]; + return + data[94] == 0 && + uint8(data[95]) == 95 && + data[96] == 0 && + uint8(data[97]) == 97; + } +} + +// ---- +// test1() -> true diff --git a/test/libsolidity/semanticTests/array/bytes_length_member.sol b/test/libsolidity/semanticTests/array/bytes_length_member.sol new file mode 100644 index 000000000..8c4872017 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_length_member.sol @@ -0,0 +1,17 @@ +contract c { + function set() public returns (bool) { + data = msg.data; + return true; + } + + function getLength() public returns (uint256) { + return data.length; + } + + bytes data; +} + +// ---- +// getLength() -> 0 +// set(): 1, 2 -> true +// getLength() -> 68 diff --git a/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol b/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol new file mode 100644 index 000000000..5b7fd7ac3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol @@ -0,0 +1,13 @@ +contract Test { + function set(bytes memory _data, uint256 i) + public + returns (uint256 l, bytes1 c) + { + l = _data.length; + c = _data[i]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(bytes,uint256): 0x40, 0x03, 0x08, "abcdefgh" -> 0x08, "d" diff --git a/test/libsolidity/semanticTests/array/calldata_array.sol b/test/libsolidity/semanticTests/array/calldata_array.sol new file mode 100644 index 000000000..c9c6dbda0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[2] calldata s) + external + pure + returns (uint256 a, uint256 b) + { + a = s[0]; + b = s[1]; + } +} + +// ---- +// f(uint256[2]): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol new file mode 100644 index 000000000..d643d3973 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol @@ -0,0 +1,21 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[][] calldata a) external returns (uint256) { + return 42; + } + + function g(uint256[][] calldata a) external returns (uint256) { + a[0]; + return 42; + } +} + +// ---- +// f(uint256[][]): 0x20, 0x0 -> 42 # valid access stub # +// f(uint256[][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # +// f(uint256[][]): 0x20, 0x1, 0x20 -> 42 # invalid on outer access # +// g(uint256[][]): 0x20, 0x1, 0x20 -> FAILURE +// f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x42 -> 42 # invalid on inner access # +// g(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x42 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol new file mode 100644 index 000000000..3efd177b2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol @@ -0,0 +1,30 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[][1][] calldata a) external returns (uint256) { + return 42; + } + + function g(uint256[][1][] calldata a) external returns (uint256) { + a[0]; + return 42; + } + + function h(uint256[][1][] calldata a) external returns (uint256) { + a[0][0]; + return 42; + } +} + +// ---- +// f(uint256[][1][]): 0x20, 0x0 -> 42 # valid access stub # +// f(uint256[][1][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # +// f(uint256[][1][]): 0x20, 0x1, 0x20 -> 42 # invalid on outer access # +// g(uint256[][1][]): 0x20, 0x1, 0x20 -> FAILURE +// f(uint256[][1][]): 0x20, 0x1, 0x20, 0x20 -> 42 # invalid on inner access # +// g(uint256[][1][]): 0x20, 0x1, 0x20, 0x20 -> 42 +// h(uint256[][1][]): 0x20, 0x1, 0x20, 0x20 -> FAILURE +// f(uint256[][1][]): 0x20, 0x1, 0x20, 0x20, 0x1 -> 42 +// g(uint256[][1][]): 0x20, 0x1, 0x20, 0x20, 0x1 -> 42 +// h(uint256[][1][]): 0x20, 0x1, 0x20, 0x20, 0x1 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol b/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol new file mode 100644 index 000000000..333112265 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S[] calldata s) + external + pure + returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) + { + l = s.length; + a = s[0].a; + b = s[0].b; + c = s[1].a; + d = s[1].b; + } +} + +// ---- +// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol b/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol new file mode 100644 index 000000000..f408746f5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol @@ -0,0 +1,25 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S[] calldata s) + external + pure + returns (uint256 l, uint256 a, uint256 b, uint256 c, uint256 d) + { + S[] memory m = s; + l = m.length; + a = m[0].a; + b = m[0].b; + c = m[1].a; + d = m[1].b; + } +} + +// ---- +// f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol b/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol new file mode 100644 index 000000000..7b225a33b --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint256[][] calldata a) + external + returns (uint256, uint256[] memory) + { + uint256[] memory m = a[0]; + return (a.length, m); + } +} + +// ---- +// f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x17, 0x2a -> 0x1, 0x40, 0x2, 0x17, 0x2a diff --git a/test/libsolidity/semanticTests/array/calldata_slice_access.sol b/test/libsolidity/semanticTests/array/calldata_slice_access.sol index 8e8a398de..7eb975677 100644 --- a/test/libsolidity/semanticTests/array/calldata_slice_access.sol +++ b/test/libsolidity/semanticTests/array/calldata_slice_access.sol @@ -6,6 +6,8 @@ contract C { return (x[start:end][index], x[start:][0:end-start][index], x[:end][start:][index]); } } +// ==== +// compileViaYul: also // ---- // f(uint256[],uint256,uint256): 0x80, 0, 0, 0, 1, 42 -> // f(uint256[],uint256,uint256): 0x80, 0, 1, 0, 1, 42 -> diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol new file mode 100644 index 000000000..017de5d7b --- /dev/null +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -0,0 +1,14 @@ +contract C { + uint256 constant LEN = 3; + uint256[LEN] public a; + + constructor(uint256[LEN] memory _a) public { + a = _a; + } +} + +// ---- +// constructor(): 1, 2, 3 -> +// a(uint256): 0 -> 1 +// a(uint256): 1 -> 2 +// a(uint256): 2 -> 3 diff --git a/test/libsolidity/semanticTests/array/copy_function_storage_array.sol b/test/libsolidity/semanticTests/array/copy_function_storage_array.sol new file mode 100644 index 000000000..eba61ad1a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copy_function_storage_array.sol @@ -0,0 +1,18 @@ +contract C { + function() internal returns (uint)[] x; + function() internal returns (uint)[] y; + + function test() public returns (uint256) { + x = new function() internal returns (uint)[](10); + x[9] = a; + y = x; + return y[9](); + } + + function a() public returns (uint256) { + return 7; + } +} + +// ---- +// test() -> 7 diff --git a/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol b/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol new file mode 100644 index 000000000..076cbbf69 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol @@ -0,0 +1,22 @@ +contract C { + function() internal returns (uint)[20] x; + int256 mutex; + + function one() public returns (uint256) { + function() internal returns (uint)[20] memory xmem; + x = xmem; + return 3; + } + + function two() public returns (uint256) { + if (mutex > 0) return 7; + mutex = 1; + // If this test fails, it might re-execute this function. + x[0](); + return 2; + } +} + +// ---- +// one() -> 3 +// two() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol new file mode 100644 index 000000000..7c2ccde6e --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint256) { + uint256[][] memory a = new uint256[][](0); + return 7; + } +} + +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/array/create_memory_array.sol b/test/libsolidity/semanticTests/array/create_memory_array.sol new file mode 100644 index 000000000..fd4fe943c --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_memory_array.sol @@ -0,0 +1,21 @@ +contract C { + struct S { + uint256[2] a; + bytes b; + } + + function f() public returns (bytes1, uint256, uint256, bytes1) { + bytes memory x = new bytes(200); + x[199] = "A"; + uint256[2][] memory y = new uint256[2][](300); + y[203][1] = 8; + S[] memory z = new S[](180); + z[170].a[1] = 4; + z[170].b = new bytes(102); + z[170].b[99] = "B"; + return (x[199], y[203][1], z[170].a[1], z[170].b[99]); + } +} + +// ---- +// f() -> "A", 8, 4, "B" diff --git a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol new file mode 100644 index 000000000..c42f84862 --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol @@ -0,0 +1,24 @@ +contract C { + function f() public returns (uint256) { + uint256 l = 2**256 / 32; + // This used to work without causing an error. + uint256[] memory x = new uint256[](l); + uint256[] memory y = new uint256[](1); + x[1] = 42; + // This used to overwrite the value written above. + y[0] = 23; + return x[1]; + } + function g() public returns (uint256) { + uint256 l = 2**256 / 2 + 1; + // This used to work without causing an error. + uint16[] memory x = new uint16[](l); + uint16[] memory y = new uint16[](1); + x[2] = 42; + // This used to overwrite the value written above. + y[0] = 23; + return x[2]; + }} +// ---- +// f() -> FAILURE +// g() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol new file mode 100644 index 000000000..731fb4312 --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol @@ -0,0 +1,34 @@ +contract C { + function f() public returns (uint256) { + uint256[][] memory x = new uint256[][](42); + assert(x[0].length == 0); + x[0] = new uint256[](1); + x[0][0] = 1; + assert(x[4].length == 0); + x[4] = new uint256[](1); + x[4][0] = 2; + assert(x[10].length == 0); + x[10] = new uint256[](1); + x[10][0] = 44; + uint256[][] memory y = new uint256[][](24); + assert(y[0].length == 0); + y[0] = new uint256[](1); + y[0][0] = 1; + assert(y[4].length == 0); + y[4] = new uint256[](1); + y[4][0] = 2; + assert(y[10].length == 0); + y[10] = new uint256[](1); + y[10][0] = 88; + if ( + (x[0][0] == y[0][0]) && + (x[4][0] == y[4][0]) && + (x[10][0] == 44) && + (y[10][0] == 88) + ) return 7; + return 0; + } +} + +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol b/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol new file mode 100644 index 000000000..316b8d9ef --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol @@ -0,0 +1,20 @@ +// Test for a bug where we did not increment the counter properly while deleting a dynamic array. +contract C { + struct S { + uint256 x; + uint256[] y; + } + S[] data; + + function f() public returns (bool) { + S storage s1 = data.push(); + s1.x = 2**200; + S storage s2 = data.push(); + s2.x = 2**200; + delete data; + return true; + } +} + +// ---- +// f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access # diff --git a/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol new file mode 100644 index 000000000..6680ec5d9 --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol @@ -0,0 +1,53 @@ +contract c { + struct Data { + uint256 x; + uint256 y; + } + Data[] data; + uint256[] ids; + + function setIDStatic(uint256 id) public { + ids[2] = id; + } + + function setID(uint256 index, uint256 id) public { + ids[index] = id; + } + + function setData(uint256 index, uint256 x, uint256 y) public { + data[index].x = x; + data[index].y = y; + } + + function getID(uint256 index) public returns (uint256) { + return ids[index]; + } + + function getData(uint256 index) public returns (uint256 x, uint256 y) { + x = data[index].x; + y = data[index].y; + } + + function getLengths() public returns (uint256 l1, uint256 l2) { + l1 = data.length; + l2 = ids.length; + } + + function setLengths(uint256 l1, uint256 l2) public { + while (data.length < l1) data.push(); + while (ids.length < l2) ids.push(); + } +} + +// ---- +// getLengths() -> 0, 0 +// setLengths(uint256,uint256): 48, 49 -> +// getLengths() -> 48, 49 +// setIDStatic(uint256): 11 -> +// getID(uint256): 2 -> 11 +// setID(uint256,uint256): 7, 8 -> +// getID(uint256): 7 -> 8 +// setData(uint256,uint256,uint256): 7, 8, 9 -> +// setData(uint256,uint256,uint256): 8, 10, 11 -> +// getData(uint256): 7 -> 8, 9 +// getData(uint256): 8 -> 10, 11 diff --git a/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol b/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol new file mode 100644 index 000000000..b2c6893a5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol @@ -0,0 +1,34 @@ +contract c { + uint256[] data; + + function enlarge(uint256 amount) public returns (uint256) { + while (data.length < amount) data.push(); + return data.length; + } + + function set(uint256 index, uint256 value) public returns (bool) { + data[index] = value; + return true; + } + + function get(uint256 index) public returns (uint256) { + return data[index]; + } + + function length() public returns (uint256) { + return data.length; + } +} + +// ==== +// compileViaYul: also +// ---- +// length() -> 0 +// get(uint256): 3 -> FAILURE +// enlarge(uint256): 4 -> 4 +// length() -> 4 +// set(uint256,uint256): 3, 4 -> true +// get(uint256): 3 -> 4 +// length() -> 4 +// set(uint256,uint256): 4, 8 -> FAILURE +// length() -> 4 diff --git a/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol new file mode 100644 index 000000000..af14b48b4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol @@ -0,0 +1,19 @@ +contract A { + uint256[3] arr; + bool public test = false; + + function getElement(uint256 i) public returns (uint256) { + return arr[i]; + } + + function testIt() public returns (bool) { + uint256 i = this.getElement(5); + test = true; + return true; + } +} + +// ---- +// test() -> false +// testIt() -> FAILURE +// test() -> false diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol new file mode 100644 index 000000000..61d1a33f5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -0,0 +1,21 @@ +contract A { + function f(uint16 input) public pure returns (uint16[5] memory arr) { + arr[0] = input; + arr[1] = ++input; + arr[2] = ++input; + arr[3] = ++input; + arr[4] = ++input; + } +} + + +contract B { + function f() public returns (uint16[5] memory res, uint16[5] memory res2) { + A a = new A(); + res = a.f(2); + res2 = a.f(1000); + } +} + +// ---- +// f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol new file mode 100644 index 000000000..ff13db5be --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol @@ -0,0 +1,14 @@ +contract Creator { + uint256 public r; + address public ch; + + constructor(address[3] memory s, uint256 x) public { + r = x; + ch = s[2]; + } +} + +// ---- +// constructor(): 1, 2, 3, 4 -> +// r() -> 4 +// ch() -> 3 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol new file mode 100644 index 000000000..b65a3d254 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol @@ -0,0 +1,45 @@ +contract c { + struct Data { + uint256 x; + uint256 y; + } + Data[2**10] data; + uint256[2**10 + 3] ids; + + function setIDStatic(uint256 id) public { + ids[2] = id; + } + + function setID(uint256 index, uint256 id) public { + ids[index] = id; + } + + function setData(uint256 index, uint256 x, uint256 y) public { + data[index].x = x; + data[index].y = y; + } + + function getID(uint256 index) public returns (uint256) { + return ids[index]; + } + + function getData(uint256 index) public returns (uint256 x, uint256 y) { + x = data[index].x; + y = data[index].y; + } + + function getLengths() public returns (uint256 l1, uint256 l2) { + l1 = data.length; + l2 = ids.length; + } +} +// ---- +// setIDStatic(uint256): 0xb -> +// getID(uint256): 0x2 -> 0xb +// setID(uint256,uint256): 0x7, 0x8 -> +// getID(uint256): 0x7 -> 0x8 +// setData(uint256,uint256,uint256): 0x7, 0x8, 0x9 -> +// setData(uint256,uint256,uint256): 0x8, 0xa, 0xb -> +// getData(uint256): 0x7 -> 0x8, 0x9 +// getData(uint256): 0x8 -> 0xa, 0xb +// getLengths() -> 0x400, 0x403 diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol new file mode 100644 index 000000000..9ab74eaf3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol @@ -0,0 +1,17 @@ +contract C { + bytes16[] public data; + + function f(bytes32 x) public returns (bytes1) { + return x[2]; + } + + function g(bytes32 x) public returns (uint256) { + data = [x[0], x[1], x[2]]; + data[0] = "12345"; + return uint256(uint8(data[0][4])); + } +} +// ---- +// f(bytes32): "789" -> "9" +// g(bytes32): "789" -> 0x35 +// data(uint256): 0x01 -> "8" diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol new file mode 100644 index 000000000..7a6afbae7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol @@ -0,0 +1,10 @@ +contract C { + bytes1 a; + + function f(bytes32 x) public returns (uint256, uint256, uint256) { + return (x.length, bytes16(uint128(2)).length, a.length + 7); + } +} + +// ---- +// f(bytes32): "789" -> 32, 16, 8 diff --git a/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol b/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol new file mode 100644 index 000000000..06246cdc6 --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol @@ -0,0 +1,28 @@ +contract c { + uint256[4] data; + + function set(uint256 index, uint256 value) public returns (bool) { + data[index] = value; + return true; + } + + function get(uint256 index) public returns (uint256) { + return data[index]; + } + + function length() public returns (uint256) { + return data.length; + } +} + +// ==== +// compileViaYul: also +// ---- +// length() -> 4 +// set(uint256,uint256): 3, 4 -> true +// set(uint256,uint256): 4, 5 -> FAILURE +// set(uint256,uint256): 400, 5 -> FAILURE +// get(uint256): 3 -> 4 +// get(uint256): 4 -> FAILURE +// get(uint256): 400 -> FAILURE +// length() -> 4 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol new file mode 100644 index 000000000..64f8c7402 --- /dev/null +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -0,0 +1,45 @@ +contract D { + function f(function() external returns (function() external returns (uint))[] memory x) + public returns (function() external returns (uint)[3] memory r) { + r[0] = x[0](); + r[1] = x[1](); + r[2] = x[2](); + } +} + + +contract C { + function test() public returns (uint256, uint256, uint256) { + function() external returns (function() external returns (uint))[] memory x = + new function() external returns (function() external returns (uint))[](10); + for (uint256 i = 0; i < x.length; i++) x[i] = this.h; + x[0] = this.htwo; + function() external returns (uint)[3] memory y = (new D()).f(x); + return (y[0](), y[1](), y[2]()); + } + + function e() public returns (uint256) { + return 5; + } + + function f() public returns (uint256) { + return 6; + } + + function g() public returns (uint256) { + return 7; + } + + uint256 counter; + + function h() public returns (function() external returns (uint)) { + return counter++ == 0 ? this.f : this.g; + } + + function htwo() public returns (function() external returns (uint)) { + return this.e; + } +} + +// ---- +// test() -> 5, 6, 7 diff --git a/test/libsolidity/semanticTests/array/function_memory_array.sol b/test/libsolidity/semanticTests/array/function_memory_array.sol new file mode 100644 index 000000000..cc6b3cf46 --- /dev/null +++ b/test/libsolidity/semanticTests/array/function_memory_array.sol @@ -0,0 +1,40 @@ +contract C { + function a(uint256 x) public returns (uint256) { + return x + 1; + } + + function b(uint256 x) public returns (uint256) { + return x + 2; + } + + function c(uint256 x) public returns (uint256) { + return x + 3; + } + + function d(uint256 x) public returns (uint256) { + return x + 5; + } + + function e(uint256 x) public returns (uint256) { + return x + 8; + } + + function test(uint256 x, uint256 i) public returns (uint256) { + function(uint) internal returns (uint)[] memory arr = + new function(uint) internal returns (uint)[](10); + arr[0] = a; + arr[1] = b; + arr[2] = c; + arr[3] = d; + arr[4] = e; + return arr[i](x); + } +} + +// ---- +// test(uint256,uint256): 10, 0 -> 11 +// test(uint256,uint256): 10, 1 -> 12 +// test(uint256,uint256): 10, 2 -> 13 +// test(uint256,uint256): 10, 3 -> 15 +// test(uint256,uint256): 10, 4 -> 18 +// test(uint256,uint256): 10, 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol b/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol new file mode 100644 index 000000000..8dc5a7451 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (uint256) { + return ([1, 2, 3, 4][2]); + } +} + +// ---- +// f() -> 3 diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol b/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol new file mode 100644 index 000000000..19dfcf3b7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol @@ -0,0 +1,15 @@ +contract C { + string public tester; + + function f() public returns (string memory) { + return (["abc", "def", "g"][0]); + } + + function test() public { + tester = f(); + } +} + +// ---- +// test() -> +// tester() -> 0x20, 0x3, "abc" diff --git a/test/libsolidity/semanticTests/array/inline_array_return.sol b/test/libsolidity/semanticTests/array/inline_array_return.sol new file mode 100644 index 000000000..e247d1558 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_return.sol @@ -0,0 +1,15 @@ +contract C { + uint8[] tester; + + function f() public returns (uint8[5] memory) { + return ([1, 2, 3, 4, 5]); + } + + function test() public returns (uint8, uint8, uint8, uint8, uint8) { + tester = f(); + return (tester[0], tester[1], tester[2], tester[3], tester[4]); + } +} + +// ---- +// f() -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/array/inline_array_singleton.sol b/test/libsolidity/semanticTests/array/inline_array_singleton.sol new file mode 100644 index 000000000..5925aba0f --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_singleton.sol @@ -0,0 +1,9 @@ +// This caused a failure since the type was not converted to its mobile type. +contract C { + function f() public returns (uint256) { + return [4][0]; + } +} + +// ---- +// f() -> 4 diff --git a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol new file mode 100644 index 000000000..f3f37ea20 --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint256 x, uint256 y) { + x = 3; + y = 6; + uint256[2] memory z = [x, y]; + return (z[0], z[1]); + } +} + +// ---- +// f() -> 3, 6 diff --git a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol new file mode 100644 index 000000000..fa9f24a7a --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol @@ -0,0 +1,12 @@ +contract C { + string s = "doh"; + + function f() public returns (string memory, string memory) { + string memory t = "ray"; + string[3] memory x = [s, t, "mi"]; + return (x[1], x[2]); + } +} + +// ---- +// f() -> 0x40, 0x80, 0x3, "ray", 0x2, "mi" diff --git a/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol b/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol new file mode 100644 index 000000000..ebcae638a --- /dev/null +++ b/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 i) public returns (string memory) { + string[4] memory x = ["This", "is", "an", "array"]; + return (x[i]); + } +} + +// ---- +// f(uint256): 0 -> 0x20, 0x4, "This" +// f(uint256): 1 -> 0x20, 0x2, "is" +// f(uint256): 2 -> 0x20, 0x2, "an" +// f(uint256): 3 -> 0x20, 0x5, "array" diff --git a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol new file mode 100644 index 000000000..be824b759 --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol @@ -0,0 +1,19 @@ +contract Test { + uint24[3][][4] data; + + function set(uint24[3][][4] memory x) + internal + returns (uint24[3][][4] memory) + { + x[1][2][2] = 1; + x[1][3][2] = 7; + return x; + } + + function f() public returns (uint24[3][] memory) { + while (data[1].length < 4) data[1].push(); + return set(data)[1]; + } +} +// ---- +// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol new file mode 100644 index 000000000..7a8a18670 --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol @@ -0,0 +1,14 @@ +contract Test { + function set(uint24[3][4] memory x) public { + x[2][2] = 1; + x[3][2] = 7; + } + + function f() public returns (uint24[3][4] memory) { + uint24[3][4] memory data; + set(data); + return data; + } +} +// ---- +// f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol b/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol new file mode 100644 index 000000000..b641ec1da --- /dev/null +++ b/test/libsolidity/semanticTests/array/memory_arrays_of_various_sizes.sol @@ -0,0 +1,17 @@ +// Computes binomial coefficients the chinese way +contract C { + function f(uint256 n, uint256 k) public returns (uint256) { + uint256[][] memory rows = new uint256[][](n + 1); + for (uint256 i = 1; i <= n; i++) { + rows[i] = new uint256[](i); + rows[i][0] = rows[i][rows[i].length - 1] = 1; + for (uint256 j = 1; j < i - 1; j++) + rows[i][j] = rows[i - 1][j - 1] + rows[i - 1][j]; + } + return rows[n][k - 1]; + } +} + +// ---- +// f(uint256,uint256): 3, 1 -> 1 +// f(uint256,uint256): 9, 5 -> 70 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol new file mode 100644 index 000000000..b2876eeb3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -0,0 +1,26 @@ +// Invoke some features that use memory and test that they do not interfere with each other. +contract Helper { + uint256 public flag; + + constructor(uint256 x) public { + flag = x; + } +} + + +contract Main { + mapping(uint256 => uint256) map; + + function f(uint256 x) public returns (uint256) { + map[x] = x; + return + (new Helper(uint256(keccak256(abi.encodePacked(this.g(map[x])))))) + .flag(); + } + + function g(uint256 a) public returns (uint256) { + return map[a]; + } +} +// ---- +// f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 diff --git a/test/libsolidity/semanticTests/array/storage_array_ref.sol b/test/libsolidity/semanticTests/array/storage_array_ref.sol new file mode 100644 index 000000000..0b0224bfc --- /dev/null +++ b/test/libsolidity/semanticTests/array/storage_array_ref.sol @@ -0,0 +1,60 @@ +contract BinarySearch { + /// Finds the position of _value in the sorted list _data. + /// Note that "internal" is important here, because storage references only work for internal or private functions + function find(uint256[] storage _data, uint256 _value) + internal + returns (uint256 o_position) + { + return find(_data, 0, _data.length, _value); + } + + function find( + uint256[] storage _data, + uint256 _begin, + uint256 _len, + uint256 _value + ) private returns (uint256 o_position) { + if (_len == 0 || (_len == 1 && _data[_begin] != _value)) + return uint256(-1); // failure + uint256 halfLen = _len / 2; + uint256 v = _data[_begin + halfLen]; + if (_value < v) return find(_data, _begin, halfLen, _value); + else if (_value > v) + return find(_data, _begin + halfLen + 1, halfLen - 1, _value); + else return _begin + halfLen; + } +} + + +contract Store is BinarySearch { + uint256[] data; + + function add(uint256 v) public { + data.push(0); + data[data.length - 1] = v; + } + + function find(uint256 v) public returns (uint256) { + return find(data, v); + } +} + +// ==== +// compileViaYul: also +// ---- +// find(uint256): 7 -> -1 +// add(uint256): 7 -> +// find(uint256): 7 -> 0 +// add(uint256): 11 -> +// add(uint256): 17 -> +// add(uint256): 27 -> +// add(uint256): 31 -> +// add(uint256): 32 -> +// add(uint256): 66 -> +// add(uint256): 177 -> +// find(uint256): 7 -> 0 +// find(uint256): 27 -> 3 +// find(uint256): 32 -> 5 +// find(uint256): 176 -> -1 +// find(uint256): 0 -> -1 +// find(uint256): 400 -> -1 diff --git a/test/libsolidity/semanticTests/array/string_allocation_bug.sol b/test/libsolidity/semanticTests/array/string_allocation_bug.sol new file mode 100644 index 000000000..9c2ec2e53 --- /dev/null +++ b/test/libsolidity/semanticTests/array/string_allocation_bug.sol @@ -0,0 +1,20 @@ +contract Sample { + struct s { + uint16 x; + uint16 y; + string a; + string b; + } + s[2] public p; + + constructor() public { + s memory m; + m.x = 0xbbbb; + m.y = 0xcccc; + m.a = "hello"; + m.b = "world"; + p[0] = m; + } +} +// ---- +// p(uint256): 0x0 -> 0xbbbb, 0xcccc, 0x80, 0xc0, 0x05, "hello", 0x05, "world" diff --git a/test/libsolidity/semanticTests/array/string_bytes_conversion.sol b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol new file mode 100644 index 000000000..9578fc4f7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol @@ -0,0 +1,17 @@ +contract Test { + string s; + bytes b; + + function f(string memory _s, uint256 n) public returns (bytes1) { + b = bytes(_s); + s = string(b); + return bytes(s)[n]; + } + + function l() public returns (uint256) { + return bytes(s).length; + } +} +// ---- +// f(string,uint256): 0x40, 0x02, 0x06, "abcdef" -> "c" +// l() -> 0x06 diff --git a/test/libsolidity/semanticTests/array/strings_in_struct.sol b/test/libsolidity/semanticTests/array/strings_in_struct.sol new file mode 100644 index 000000000..a6ec607f4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/strings_in_struct.sol @@ -0,0 +1,35 @@ +contract buggystruct { + Buggy public bug; + + struct Buggy { + uint256 first; + uint256 second; + uint256 third; + string last; + } + + constructor() public { + bug = Buggy(10, 20, 30, "asdfghjkl"); + } + + function getFirst() public returns (uint256) { + return bug.first; + } + + function getSecond() public returns (uint256) { + return bug.second; + } + + function getThird() public returns (uint256) { + return bug.third; + } + + function getLast() public returns (string memory) { + return bug.last; + } +} +// ---- +// getFirst() -> 0x0a +// getSecond() -> 0x14 +// getThird() -> 0x1e +// getLast() -> 0x20, 0x09, "asdfghjkl" diff --git a/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol new file mode 100644 index 000000000..2fc479d0e --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol @@ -0,0 +1,9 @@ +contract C { + bytes32 constant x = keccak256("abc"); + + function f() public returns (bytes32) { + return x; + } +} +// ---- +// f() -> 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 diff --git a/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol new file mode 100644 index 000000000..2e771032b --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol @@ -0,0 +1,26 @@ +contract C { + uint256 public x; + + function f() public pure returns (bytes4) { + return this.f.selector; + } + + function g() public returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function h() public returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function i() public pure returns (bytes4) { + return this.x.selector; + } +} +// ---- +// f() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// g() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// h() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// i() -> 0x0c55699c00000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol new file mode 100644 index 000000000..91a26945c --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function foo() public returns (bytes32) { + data.push("x"); + data.push("y"); + data.push("z"); + return keccak256(abi.encodePacked("b", keccak256(data), "a")); + } +} +// ---- +// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol new file mode 100644 index 000000000..1374538c2 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (bytes32) { + return keccak256(""); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol new file mode 100644 index 000000000..972aee839 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol @@ -0,0 +1,7 @@ +contract c { + function foo(uint256 a, uint256 b, uint256 c) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, c)); + } +} +// ---- +// foo(uint256,uint256,uint256): 0xa, 0xc, 0xd -> 0xbc740a98aae5923e8f04c9aa798c9ee82f69e319997699f2782c40828db9fd81 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol new file mode 100644 index 000000000..01397f55f --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol @@ -0,0 +1,7 @@ +contract c { + function foo(uint256 a, uint16 b) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, uint8(145))); + } +} +// ---- +// foo(uint256,uint16): 0xa, 0xc -> 0x88acd45f75907e7c560318bc1a5249850a0999c4896717b1167d05d116e6dbad diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol new file mode 100644 index 000000000..b157178fb --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol @@ -0,0 +1,12 @@ +contract c { + function foo() public returns (bytes32 d) { + d = keccak256("foo"); + } + + function bar(uint256 a, uint16 b) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); + } +} +// ---- +// foo() -> 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d +// bar(uint256,uint16): 0xa, 0xc -> 0x6990f36476dc412b1c4baa48e2d9f4aa4bb313f61fda367c8fdbbb2232dc6146 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol new file mode 100644 index 000000000..725d984d8 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol @@ -0,0 +1,13 @@ +contract c { + bytes data; + + function foo() public returns (bool) { + data.push("f"); + data.push("o"); + data.push("o"); + return keccak256(data) == keccak256("foo"); + } +} + +// ---- +// foo() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol new file mode 100644 index 000000000..11b9a5339 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol @@ -0,0 +1,9 @@ +contract test { + function foo(uint256 a) public returns (bytes4 value) { + return msg.sig; + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol new file mode 100644 index 000000000..646f8f6e9 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol @@ -0,0 +1,13 @@ +contract test { + function boo() public returns (bytes4 value) { + return msg.sig; + } + + function foo(uint256 a) public returns (bytes4 value) { + return boo(); + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol new file mode 100644 index 000000000..c79625d3c --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (bytes20) { + return ripemd160(""); + } +} + +// ---- +// f() -> 0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol new file mode 100644 index 000000000..69b9e15f5 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (bytes32) { + return sha256(""); + } +} + +// ---- +// f() -> 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol new file mode 100644 index 000000000..c2c2fa70a --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol @@ -0,0 +1,74 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f1(bytes[1] calldata a) + external + returns (uint256, uint256, uint256, uint256) + { + return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2])); + } + + function f2(bytes[1] calldata a, bytes[1] calldata b) + external + returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) + { + return ( + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + uint8(a[0][2]), + b[0].length, + uint8(b[0][0]), + uint8(b[0][1]) + ); + } + + function g1(bytes[2] calldata a) + external + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + uint8(a[0][2]), + a[1].length, + uint8(a[1][0]), + uint8(a[1][1]), + uint8(a[1][2]) + ); + } + + function g2(bytes[] calldata a) external returns (uint256[8] memory) { + return [ + a.length, + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + a[1].length, + uint8(a[1][0]), + uint8(a[1][1]), + uint8(a[1][2]) + ]; + } +} + +// found expectation comments: +// same offset for both arrays @ ABI_CHECK( + +// ---- +// f1(bytes[1]): 0x20, 0x20, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3 +// f2(bytes[1],bytes[1]): 0x40, 0xa0, 0x20, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000", 0x20, 0x2, hex"0102000000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x2, 0x1, 0x2 +// g1(bytes[2]): 0x20, 0x40, 0x80, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000", 0x3, hex"0405060000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x3, 0x4, 0x5, 0x6 +// g1(bytes[2]): 0x20, 0x40, 0x40, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x3, 0x1, 0x2, 0x3 +// g2(bytes[]): 0x20, 0x2, 0x40, 0x80, 0x2, hex"0102000000000000000000000000000000000000000000000000000000000000", 0x3, hex"0405060000000000000000000000000000000000000000000000000000000000" -> 0x2, 0x2, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 diff --git a/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol b/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol new file mode 100644 index 000000000..04b2ed15d --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bytes[] calldata a) + external + returns (uint256, uint256, bytes memory) + { + bytes memory m = a[0]; + return (a.length, m.length, m); + } +} +// ---- +// f(bytes[]): 0x20, 0x1, 0x20, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" -> 0x1, 0x2, 0x60, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"7878787878787878787878787878787878787878787878787878787878787878" -> 0x1, 0x20, 0x60, 0x20, hex"7878787878787878787878787878787878787878787878787878787878787878" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"7800000000000000000000000000000000000000000000000000000000000061" -> 0x1, 0x20, 0x60, 0x20, hex"7800000000000000000000000000000000000000000000000000000000000061" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"6100000000000000000000000000000000000000000000000000000000000078" -> 0x1, 0x20, 0x60, 0x20, hex"6100000000000000000000000000000000000000000000000000000000000078" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"616d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d78" -> 0x1, 0x20, 0x60, 0x20, hex"616d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d78" diff --git a/test/libsolidity/semanticTests/calldata/calldata_string_array.sol b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol new file mode 100644 index 000000000..ae7d1e2ef --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(string[] calldata a) + external + returns (uint256, uint256, uint256, string memory) + { + string memory s1 = a[0]; + bytes memory m1 = bytes(s1); + return (a.length, m1.length, uint8(m1[0]), s1); + } +} +// ---- +// f(string[]): 0x20, 0x1, 0x20, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" -> 1, 2, 97, 0x80, 2, "ab" diff --git a/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol new file mode 100644 index 000000000..dbf6e4440 --- /dev/null +++ b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint8 a; + bytes1 b; + } + + function f(S calldata s) external pure returns (uint256 a, bytes32 b) { + uint8 tmp1 = s.a; + bytes1 tmp2 = s.b; + assembly { + a := tmp1 + b := tmp2 + } + } +} +// ---- +// f((uint8,bytes1)): 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" -> 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" # double check that the valid case goes through # +// f((uint8,bytes1)): 0x1234, hex"5678000000000000000000000000000000000000000000000000000000000000" -> FAILURE +// f((uint8,bytes1)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/bool_conversion.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion.sol new file mode 100644 index 000000000..20f594764 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/bool_conversion.sol @@ -0,0 +1,23 @@ +contract C { + function f(bool _b) public returns (uint256) { + if (_b) return 1; + else return 0; + } + + function g(bool _in) public returns (bool _out) { + _out = _in; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bool): 0x0 -> 0x0 +// f(bool): 0x1 -> 0x1 +// f(bool): 0x2 -> 0x1 +// f(bool): 0x3 -> 0x1 +// f(bool): 0xff -> 0x1 +// g(bool): 0x0 -> 0x0 +// g(bool): 0x1 -> 0x1 +// g(bool): 0x2 -> 0x1 +// g(bool): 0x3 -> 0x1 +// g(bool): 0xff -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol new file mode 100644 index 000000000..a8fd89833 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bool _b) public returns (uint256) { + if (_b) return 1; + else return 0; + } + + function g(bool _in) public returns (bool _out) { + _out = _in; + } +} +// ---- +// f(bool): 0x0 -> 0x0 +// f(bool): 0x1 -> 0x1 +// f(bool): 0x2 -> FAILURE +// f(bool): 0x3 -> FAILURE +// f(bool): 0xff -> FAILURE +// g(bool): 0x0 -> 0x0 +// g(bool): 0x1 -> 0x1 +// g(bool): 0x2 -> FAILURE +// g(bool): 0x3 -> FAILURE +// g(bool): 0xff -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol new file mode 100644 index 000000000..c94cfc776 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol @@ -0,0 +1,17 @@ +// Checks that address types are properly cleaned before they are compared. +contract C { + function f(address a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + + function g(address payable a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(address): 0xffff1234567890123456789012345678901234567890 -> 0x0 # We input longer data on purpose.# +// g(address): 0xffff1234567890123456789012345678901234567890 -> 0x0 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol new file mode 100644 index 000000000..074b3ff6b --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol @@ -0,0 +1,33 @@ +contract C { + function f() public pure returns (address r) { + bytes21 x = 0x1122334455667788990011223344556677889900ff; + bytes20 y; + assembly { + y := x + } + address z = address(y); + assembly { + r := z + } + require(z == 0x1122334455667788990011223344556677889900); + } + + function g() public pure returns (address payable r) { + bytes21 x = 0x1122334455667788990011223344556677889900ff; + bytes20 y; + assembly { + y := x + } + address payable z = address(y); + assembly { + r := z + } + require(z == 0x1122334455667788990011223344556677889900); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1122334455667788990011223344556677889900 +// g() -> 0x1122334455667788990011223344556677889900 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol new file mode 100644 index 000000000..beff156f7 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + + +// Checks that address types are properly cleaned before they are compared. +contract C { + function f(address a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + + function g(address payable a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } +} +// ---- +// f(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE # We input longer data on purpose.# +// g(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol new file mode 100644 index 000000000..32b90748e --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol @@ -0,0 +1,13 @@ +// Checks that bytesXX types are properly cleaned before they are compared. +contract C { + function f(bytes2 a, uint16 x) public returns (uint256) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(uint24(x)) != 0x000102) return 3; + return 0; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bytes2,uint16): "abc", 0x40102 -> 0x0 # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol new file mode 100644 index 000000000..dc7c7f890 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol @@ -0,0 +1,15 @@ +contract C { + function f() public pure returns (bytes32 r) { + bytes4 x = 0xffffffff; + bytes2 y = bytes2(x); + assembly { + r := y + } + // At this point, r and y both store four bytes, but + // y is properly cleaned before the equality check + require(y == bytes2(0xffff)); + } +} + +// ---- +// f() -> "\xff\xff\xff\xff" diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol new file mode 100644 index 000000000..5adc97378 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +// Checks that bytesXX types are properly cleaned before they are compared. +contract C { + function f(bytes2 a, uint16 x) public returns (uint256) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(uint24(x)) != 0x000102) return 3; + return 0; + } +} +// ---- +// f(bytes2,uint16): "abc", 0x40102 -> FAILURE # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol new file mode 100644 index 000000000..c1901c738 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol @@ -0,0 +1,13 @@ +contract C { + function test() public returns (uint256, uint256) { + uint32 a = 0xffffffff; + uint16 x = uint16(a); + uint16 y = x; + x /= 0x100; + y = y / 0x100; + return (x, y); + } +} + +// ---- +// test() -> 0xff, 0xff diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol new file mode 100644 index 000000000..a81e2755e --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (uint8 x) { + uint8 y = uint8(2)**uint8(8); + return 0**y; + } +} + +// ---- +// f() -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol new file mode 100644 index 000000000..a9b5e81b5 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint8 x) { + return uint8(0)**uint8(uint8(2)**uint8(8)); + } +} + +// ---- +// f() -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol new file mode 100644 index 000000000..4f817d186 --- /dev/null +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint8 x) { + return uint8(0x166)**uint8(uint8(2)**uint8(8)); + } +} + +// ---- +// f() -> 0x1 diff --git a/test/libsolidity/semanticTests/constants/constant_string.sol b/test/libsolidity/semanticTests/constants/constant_string.sol new file mode 100644 index 000000000..56cbf982d --- /dev/null +++ b/test/libsolidity/semanticTests/constants/constant_string.sol @@ -0,0 +1,22 @@ +contract C { + bytes constant a = "\x03\x01\x02"; + bytes constant b = hex"030102"; + string constant c = "hello"; + + function f() public returns (bytes memory) { + return a; + } + + function g() public returns (bytes memory) { + return b; + } + + function h() public returns (bytes memory) { + return bytes(c); + } +} + +// ---- +// f() -> 0x20, 3, "\x03\x01\x02" +// g() -> 0x20, 3, "\x03\x01\x02" +// h() -> 0x20, 5, "hello" diff --git a/test/libsolidity/semanticTests/constants/constant_variables.sol b/test/libsolidity/semanticTests/constants/constant_variables.sol new file mode 100644 index 000000000..98b4773c7 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/constant_variables.sol @@ -0,0 +1,11 @@ +contract Foo { + uint256 constant x = 56; + enum ActionChoices {GoLeft, GoRight, GoStraight, Sit} + ActionChoices constant choices = ActionChoices.GoLeft; + bytes32 constant st = "abc\x00\xff__"; +} + +// ==== +// compileViaYul: also +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol new file mode 100644 index 000000000..c05060b7e --- /dev/null +++ b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol @@ -0,0 +1,10 @@ +contract Foo { + function getX() public returns (uint256 r) { + return x; + } + + uint256 constant x = 56; +} + +// ---- +// getX() -> 56 diff --git a/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol b/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol new file mode 100644 index 000000000..2384c061f --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/base_constructor_arguments.sol @@ -0,0 +1,24 @@ +contract BaseBase { + uint256 m_a; + + constructor(uint256 a) public { + m_a = a; + } +} + + +contract Base is BaseBase(7) { + constructor() public { + m_a *= m_a; + } +} + + +contract Derived is Base { + function getA() public returns (uint256 r) { + return m_a; + } +} + +// ---- +// getA() -> 49 diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol new file mode 100644 index 000000000..ec30bacf6 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol @@ -0,0 +1,22 @@ +contract Main { + bytes3 name; + bool flag; + + constructor(bytes3 x, bool f) public { + name = x; + flag = f; + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function getFlag() public returns (bool ret) { + return flag; + } +} + +// ---- +// constructor(): "abc", true +// getFlag() -> true +// getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol new file mode 100644 index 000000000..756478e34 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_internal.sol @@ -0,0 +1,38 @@ +contract Helper { + bytes3 name; + bool flag; + + constructor(bytes3 x, bool f) public { + name = x; + flag = f; + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function getFlag() public returns (bool ret) { + return flag; + } +} + + +contract Main { + Helper h; + + constructor() public { + h = new Helper("abc", true); + } + + function getFlag() public returns (bool ret) { + return h.getFlag(); + } + + function getName() public returns (bytes3 ret) { + return h.getName(); + } +} + +// ---- +// getFlag() -> true +// getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol new file mode 100644 index 000000000..10243f897 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -0,0 +1,16 @@ +contract C { + uint256 public a; + uint256[3] public b; + + constructor(uint256 _a, uint256[3] memory _b) public { + a = _a; + b = _b; + } +} + +// ---- +// constructor(): 1, 2, 3, 4 -> +// a() -> 1 +// b(uint256): 0 -> 2 +// b(uint256): 1 -> 3 +// b(uint256): 2 -> 4 diff --git a/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol b/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol new file mode 100644 index 000000000..2192dabf3 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/evm_exceptions_in_constructor_call_fail.sol @@ -0,0 +1,19 @@ +contract A { + constructor() public { + address(this).call("123"); + } +} + + +contract B { + uint256 public test = 1; + + function testIt() public { + A a = new A(); + ++test; + } +} + +// ---- +// testIt() -> +// test() -> 2 diff --git a/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol b/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol new file mode 100644 index 000000000..e92a37b19 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/function_usage_in_constructor_arguments.sol @@ -0,0 +1,24 @@ +contract BaseBase { + uint256 m_a; + + constructor(uint256 a) public { + m_a = a; + } + + function g() public returns (uint256 r) { + return 2; + } +} + + +contract Base is BaseBase(BaseBase.g()) {} + + +contract Derived is Base { + function getA() public returns (uint256 r) { + return m_a; + } +} + +// ---- +// getA() -> 2 diff --git a/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol new file mode 100644 index 000000000..42db8582a --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol @@ -0,0 +1,21 @@ +contract Test { + bytes3 name; + bool flag; + + constructor() public { + setName("abc"); + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function setName(bytes3 _name) private { + name = _name; + } +} + +// ==== +// compileViaYul: also +// ---- +// getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol new file mode 100644 index 000000000..0aea44e6a --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol @@ -0,0 +1,20 @@ +contract Base { + uint256 m_base = 5; + + function getBMember() public returns (uint256 i) { + return m_base; + } +} + + +contract Derived is Base { + uint256 m_derived = 6; + + function getDMember() public returns (uint256 i) { + return m_derived; + } +} + +// ---- +// getBMember() -> 5 +// getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/constructor/payable_constructor.sol b/test/libsolidity/semanticTests/constructor/payable_constructor.sol new file mode 100644 index 000000000..9a3c56ebe --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/payable_constructor.sol @@ -0,0 +1,8 @@ +contract C { + constructor() public payable {} +} + +// ==== +// compileViaYul: also +// ---- +// constructor(), 27 wei -> diff --git a/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol new file mode 100644 index 000000000..ab8ad47f9 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol @@ -0,0 +1,21 @@ +contract C { + uint256 public result_in_constructor; + function(uint256) returns (uint256) internal x; + + constructor() public { + x = double; + result_in_constructor = use(2); + } + + function double(uint256 _arg) public returns (uint256 _ret) { + _ret = _arg * 2; + } + + function use(uint256 _arg) public returns (uint256) { + return x(_arg); + } +} + +// ---- +// use(uint256): 3 -> 6 +// result_in_constructor() -> 4 diff --git a/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol new file mode 100644 index 000000000..7492a4281 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol @@ -0,0 +1,18 @@ +contract C { + function() returns (uint256) internal x; + + constructor() public { + x = unused; + } + + function unused() internal returns (uint256) { + return 7; + } + + function t() public returns (uint256) { + return x(); + } +} + +// ---- +// t() -> 7 diff --git a/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol new file mode 100644 index 000000000..0a4295114 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol @@ -0,0 +1,21 @@ +library L { + function x() internal returns (uint256) { + return 7; + } +} + + +contract C { + function() returns (uint256) internal x; + + constructor() public { + x = L.x; + } + + function t() public returns (uint256) { + return x(); + } +} + +// ---- +// t() -> 7 diff --git a/test/libsolidity/semanticTests/constructor_with_params.sol b/test/libsolidity/semanticTests/constructor_with_params.sol new file mode 100644 index 000000000..19f0bdb57 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor_with_params.sol @@ -0,0 +1,15 @@ +contract C { + uint public i; + uint public k; + + constructor(uint newI, uint newK) public { + i = newI; + k = newK; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor(): 2, 0 -> +// i() -> 2 +// k() -> 0 diff --git a/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol b/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol new file mode 100644 index 000000000..9ff75fad6 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol @@ -0,0 +1,12 @@ +contract c { + enum Truth {False, True} + + function test() public returns (uint256) { + return uint256(Truth(uint8(0x701))); + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> 1 diff --git a/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol b/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol new file mode 100644 index 000000000..fd4c96ce7 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol @@ -0,0 +1,31 @@ +contract test { + enum ActionChoices {GoLeft, GoRight, GoStraight} + + constructor() public {} + + function getChoiceExp(uint256 x) public returns (uint256 d) { + choice = ActionChoices(x); + d = uint256(choice); + } + + function getChoiceFromSigned(int256 x) public returns (uint256 d) { + choice = ActionChoices(x); + d = uint256(choice); + } + + function getChoiceFromNegativeLiteral() public returns (uint256 d) { + choice = ActionChoices(-1); + d = uint256(choice); + } + + ActionChoices choice; +} + +// ==== +// compileViaYul: also +// ---- +// getChoiceExp(uint256): 3 -> FAILURE # These should throw # +// getChoiceFromSigned(int256): -1 -> FAILURE +// getChoiceFromNegativeLiteral() -> FAILURE +// getChoiceExp(uint256): 2 -> 2 # These should work # +// getChoiceExp(uint256): 0 -> 0 diff --git a/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol b/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol new file mode 100644 index 000000000..5d60a7466 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol @@ -0,0 +1,10 @@ +contract test { + enum Choice {A, B, C} + + function answer() public returns (test.Choice _ret) { + _ret = test.Choice.B; + } +} + +// ---- +// answer() -> 1 diff --git a/test/libsolidity/semanticTests/enums/using_enums.sol b/test/libsolidity/semanticTests/enums/using_enums.sol new file mode 100644 index 000000000..ae497f409 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_enums.sol @@ -0,0 +1,16 @@ +contract test { + enum ActionChoices {GoLeft, GoRight, GoStraight, Sit} + + constructor() public { + choices = ActionChoices.GoStraight; + } + + function getChoice() public returns (uint256 d) { + d = uint256(choices); + } + + ActionChoices choices; +} + +// ---- +// getChoice() -> 2 diff --git a/test/libsolidity/semanticTests/enums/using_inherited_enum.sol b/test/libsolidity/semanticTests/enums/using_inherited_enum.sol new file mode 100644 index 000000000..c39bfe064 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_inherited_enum.sol @@ -0,0 +1,13 @@ +contract base { + enum Choice {A, B, C} +} + + +contract test is base { + function answer() public returns (Choice _ret) { + _ret = Choice.B; + } +} + +// ---- +// answer() -> 1 diff --git a/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol b/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol new file mode 100644 index 000000000..0be3f80d4 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol @@ -0,0 +1,13 @@ +contract base { + enum Choice {A, B, C} +} + + +contract test is base { + function answer() public returns (base.Choice _ret) { + _ret = base.Choice.B; + } +} + +// ---- +// answer() -> 1 diff --git a/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol b/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol new file mode 100644 index 000000000..5d93ba0ee --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol @@ -0,0 +1,28 @@ +contract test { + function f0() public returns (uint) { + return 2; + } + + function f1() internal returns (function() internal returns (uint)) { + return f0; + } + + function f2() internal returns (function() internal returns (function () internal returns (uint))) { + return f1; + } + + function f3() internal returns (function() internal returns (function () internal returns (function () internal returns (uint)))) { + return f2; + } + + function f() public returns (uint) { + function() internal returns(function() internal returns(function() internal returns(function() internal returns(uint)))) x; + x = f3; + return x()()()(); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol b/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol new file mode 100644 index 000000000..2ccfc064c --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol @@ -0,0 +1,28 @@ +abstract contract D { + function g() public virtual; +} + + +contract C { + D d = D(0x1212); + + function f() public returns (uint256) { + d.g(); + return 7; + } + + function g() public returns (uint256) { + d.g.gas(200)(); + return 7; + } + + function h() public returns (uint256) { + address(d).call(""); // this does not throw (low-level) + return 7; + } +} + +// ---- +// f() -> FAILURE +// g() -> FAILURE +// h() -> 7 diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol new file mode 100644 index 000000000..e1cab8fe8 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol @@ -0,0 +1,17 @@ +contract C { + function intern() public returns (uint256) { + function (uint) internal returns (uint) x; + x(2); + return 7; + } + + function extern() public returns (uint256) { + function (uint) external returns (uint) x; + x(2); + return 7; + } +} + +// ---- +// intern() -> FAILURE # This should throw exceptions # +// extern() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol new file mode 100644 index 000000000..0ae431b24 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol @@ -0,0 +1,20 @@ +contract C { + function() returns (uint256) internal x; + int256 mutex; + + function t() public returns (uint256) { + if (mutex > 0) { + assembly { + mstore(0, 7) + return(0, 0x20) + } + } + mutex = 1; + // Avoid re-executing this function if we jump somewhere. + x(); + return 2; + } +} + +// ---- +// t() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol new file mode 100644 index 000000000..26a2a79f4 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol @@ -0,0 +1,20 @@ +contract C { + int256 mutex; + + function t() public returns (uint256) { + if (mutex > 0) { + assembly { + mstore(0, 7) + return(0, 0x20) + } + } + mutex = 1; + // Avoid re-executing this function if we jump somewhere. + function() internal returns (uint)[200] memory x; + x[0](); + return 2; + } +} + +// ---- +// t() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol new file mode 100644 index 000000000..ffa22b8a2 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol @@ -0,0 +1,37 @@ +contract C { + uint256 value; + + function set(uint256 _value) external { + value = _value; + } + + function get() external view returns (uint256) { + return value; + } + + function get_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("get()")); + } + + function assert0() external view { + assert(value == 0); + } + + function assert0_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("assert0()")); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// get() -> 0x00 +// assert0_delegated() -> 0x01, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x0 +// set(uint256): 0x01 -> +// get() -> 0x01 +// assert0_delegated() -> 0x00, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x1 +// set(uint256): 0x2a -> +// get() -> 0x2a +// assert0_delegated() -> 0x00, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x2a diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol new file mode 100644 index 000000000..498449d17 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol @@ -0,0 +1,39 @@ +contract C { + uint256 value; + + function set(uint256 _value) external { + value = _value; + } + + function get() external view returns (uint256) { + return value; + } + + function get_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); + return success; + } + + function assert0() external view { + assert(value == 0); + } + + function assert0_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); + return success; + } +} +// ==== +// EVMVersion: 0x00 +// assert0_delegated() -> true +// get_delegated() -> true +// set(uint256): 0x01 -> +// get() -> 0x01 +// assert0_delegated() -> false +// get_delegated() -> true +// set(uint256): 0x2a -> +// get() -> 0x2a +// assert0_delegated() -> false +// get_delegated() -> true diff --git a/test/libsolidity/semanticTests/functionCall/external_call_value.sol b/test/libsolidity/semanticTests/functionCall/external_call_value.sol new file mode 100644 index 000000000..47e2bfb76 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_call_value.sol @@ -0,0 +1,17 @@ +pragma solidity >= 0.6.0; + +contract C { + function g(uint n) external payable returns (uint, uint) { + return (msg.value * 1000, n); + } + + function f(uint n) public payable returns (uint, uint) { + return this.g{value: 10}(n); + } +} + +// ==== +// compileViaYul: also +// ---- +// g(uint256), 1 ether: 4 -> 1000000000000000000000, 4 +// f(uint256), 11 ether: 2 -> 10000, 2 diff --git a/test/libsolidity/semanticTests/functionCall/external_function.sol b/test/libsolidity/semanticTests/functionCall/external_function.sol new file mode 100644 index 000000000..362586447 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_function.sol @@ -0,0 +1,18 @@ +contract c { + function f(uint256 a) public returns (uint256) { + return a; + } + + function test(uint256 a, uint256 b) + external + returns (uint256 r_a, uint256 r_b) + { + r_a = f(a + 7); + r_b = b; + } +} + +// ==== +// compileViaYul: also +// ---- +// test(uint256,uint256): 2, 3 -> 9, 3 diff --git a/test/libsolidity/semanticTests/functionCall/external_public_override.sol b/test/libsolidity/semanticTests/functionCall/external_public_override.sol new file mode 100644 index 000000000..7909f8e32 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/external_public_override.sol @@ -0,0 +1,22 @@ +contract A { + function f() external virtual returns (uint256) { + return 1; + } +} + + +contract B is A { + function f() public override returns (uint256) { + return 2; + } + + function g() public returns (uint256) { + return f(); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 2 +// g() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol new file mode 100644 index 000000000..aaafb6f28 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -0,0 +1,44 @@ +contract helper { + bool flag; + + function getBalance() public payable returns (uint256 myBalance) { + return address(this).balance; + } + + function setFlag() public { + flag = true; + } + + function getFlag() public returns (bool fl) { + return flag; + } +} + + +contract test { + helper h; + + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint256 amount) public payable returns (uint256 bal) { + return h.getBalance.value(amount)(); + } + + function outOfGas() public returns (bool ret) { + h.setFlag.gas(2)(); // should fail due to OOG + return true; + } + + function checkState() public returns (bool flagAfter, uint256 myBal) { + flagAfter = h.getFlag(); + myBal = address(this).balance; + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 5 +// outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # +// checkState() -> false, 15 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol new file mode 100644 index 000000000..dbd524deb --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -0,0 +1,43 @@ +contract helper { + bool flag; + + function getBalance() payable public returns(uint256 myBalance) { + return address(this).balance; + } + + function setFlag() public { + flag = true; + } + + function getFlag() public returns(bool fl) { + return flag; + } +} +contract test { + helper h; + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint amount) public payable returns(uint256 bal) { + return h.getBalance{value: amount}(); + } + + function outOfGas() public returns(bool ret) { + h.setFlag { + gas: 2 + }(); // should fail due to OOG + return true; + } + + function checkState() public returns(bool flagAfter, uint myBal) { + flagAfter = h.getFlag(); + myBal = address(this).balance; + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 5 +// outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # +// checkState() -> false, 15 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol new file mode 100644 index 000000000..e0c6ff5e0 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -0,0 +1,19 @@ +// Sending zero ether to a contract should still invoke the receive ether function +// (it previously did not because the gas stipend was not provided by the EVM) +contract Receiver { + receive() external payable {} +} + + +contract Main { + constructor() public payable {} + + function s() public returns (bool) { + Receiver r = new Receiver(); + return address(r).send(0); + } +} + +// ---- +// constructor(), 20 wei -> +// s() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol b/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol new file mode 100644 index 000000000..96a66fce2 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol @@ -0,0 +1,16 @@ +contract C { + function a() public returns (uint256) { + return 7; + } + + function test() public returns (uint256) { + function() returns (uint256) y = a; + delete y; + y(); + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol b/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol new file mode 100644 index 000000000..6c7ecf4f6 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol @@ -0,0 +1,27 @@ +contract C { + function a() public returns (uint256) { + return 7; + } + + function() returns (uint256) internal y; + + function set() public returns (uint256) { + y = a; + return y(); + } + + function d() public returns (uint256) { + delete y; + return 1; + } + + function ca() public returns (uint256) { + return y(); + } +} + +// ---- +// set() -> 7 +// ca() -> 7 +// d() -> 1 +// ca() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionTypes/function_type_library_internal.sol b/test/libsolidity/semanticTests/functionTypes/function_type_library_internal.sol new file mode 100644 index 000000000..f096a4979 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/function_type_library_internal.sol @@ -0,0 +1,26 @@ +library Utils { + function reduce( + uint256[] memory array, + function(uint, uint) internal returns (uint) f, + uint256 init + ) internal returns (uint256) { + for (uint256 i = 0; i < array.length; i++) { + init = f(array[i], init); + } + return init; + } + + function sum(uint256 a, uint256 b) internal returns (uint256) { + return a + b; + } +} + + +contract C { + function f(uint256[] memory x) public returns (uint256) { + return Utils.reduce(x, Utils.sum, 0); + } +} + +// ---- +// f(uint256[]): 0x20, 0x3, 0x1, 0x7, 0x3 -> 11 diff --git a/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol b/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol new file mode 100644 index 000000000..ac802ee9e --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol @@ -0,0 +1,34 @@ +contract Flow { + bool public success; + + mapping(address => function() internal) stages; + + function stage0() internal { + stages[msg.sender] = stage1; + } + + function stage1() internal { + stages[msg.sender] = stage2; + } + + function stage2() internal { + success = true; + } + + constructor() public { + stages[msg.sender] = stage0; + } + + function f() public returns (uint256) { + stages[msg.sender](); + return 7; + } +} + +// ---- +// success() -> false +// f() -> 7 +// f() -> 7 +// success() -> false +// f() -> 7 +// success() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol new file mode 100644 index 000000000..ebef9a254 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_externally.sol @@ -0,0 +1,21 @@ +contract C { + function f(uint256 x) public returns (uint256) { + return this.eval(this.g, x); + } + + function f2(uint256 x) public returns (uint256) { + return eval(this.g, x); + } + + function eval(function(uint) external returns (uint) x, uint a) public returns (uint) { + return x(a); + } + + function g(uint256 x) public returns (uint256) { + return x + 1; + } +} + +// ---- +// f(uint256): 7 -> 8 +// f2(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol new file mode 100644 index 000000000..6fb4f5f6e --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint256 x) public returns (uint256) { + return eval(g, x); + } + + function eval(function(uint) internal returns (uint) x, uint a) internal returns (uint) { + return x(a); + } + + function g(uint256 x) public returns (uint256) { + return x + 1; + } +} + +// ---- +// f(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol new file mode 100644 index 000000000..a79cb2d76 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol @@ -0,0 +1,19 @@ +contract C { + uint256 public initial; + + constructor() public { + initial = double(2); + } + + function double(uint256 _arg) public returns (uint256 _ret) { + _ret = _arg * 2; + } + + function runtime(uint256 _arg) public returns (uint256) { + return double(_arg); + } +} + +// ---- +// runtime(uint256): 3 -> 6 +// initial() -> 4 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol new file mode 100644 index 000000000..f8230b21a --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol @@ -0,0 +1,18 @@ +contract C { + function(uint256) returns (uint256) internal x; + + constructor() public { + x = double; + } + + function test() public returns (bool) { + return x == double; + } + + function double(uint256 _arg) public returns (uint256 _ret) { + _ret = _arg * 2; + } +} + +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol new file mode 100644 index 000000000..a09404fce --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -0,0 +1,28 @@ +contract Other { + function addTwo(uint256 x) public returns (uint256) { + return x + 2; + } +} + + +contract C { + function (function (uint) external returns (uint)) internal returns (uint) ev; + function (uint) external returns (uint) x; + + function store(function(uint) external returns (uint) y) public { + x = y; + } + + function eval(function(uint) external returns (uint) y) public returns (uint) { + return y(7); + } + + function t() public returns (uint256) { + ev = eval; + this.store((new Other()).addTwo); + return ev(x); + } +} + +// ---- +// t() -> 9 diff --git a/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol new file mode 100644 index 000000000..ca1055fd6 --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol @@ -0,0 +1,11 @@ +contract Test { + function() internal x; + + function f() public returns (uint256 r) { + x(); + return 2; + } +} + +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol b/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol new file mode 100644 index 000000000..3f71a31e0 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol @@ -0,0 +1,8 @@ +contract A { + uint8 immutable a = 2; + function f() public view returns (uint) { + return a; + } +} +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/immutable/getter.sol b/test/libsolidity/semanticTests/immutable/getter.sol new file mode 100644 index 000000000..bb4b191cf --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/getter.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable public x = 1; +} +// ---- +// x() -> 1 diff --git a/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol new file mode 100644 index 000000000..e4aa474f2 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol @@ -0,0 +1,17 @@ +contract A { + uint immutable public x = 1; + uint public y; + constructor() public { + y = this.x(); + } +} +contract C { + function f() public returns (bool) { + try new A() { return false; } + catch { return true; } + } +} +// ==== +// EVMVersion: >=tangerineWhistle +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/immutable/inheritance.sol b/test/libsolidity/semanticTests/immutable/inheritance.sol new file mode 100644 index 000000000..f61009f60 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/inheritance.sol @@ -0,0 +1,30 @@ +contract A { + uint8 immutable a; + constructor() public { + a = 4; + } +} +contract B is A { + uint8 immutable b; + constructor() public { + b = 3; + } +} +contract C is A { + uint8 immutable c; + constructor() public { + c = 2; + } +} +contract D is B, C { + uint8 immutable d; + + constructor() public { + d = 1; + } + function f() public view returns (uint256, uint256, uint, uint) { + return (a, b, c, d); + } +} +// ---- +// f() -> 4, 3, 2, 1 diff --git a/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol b/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol new file mode 100644 index 000000000..0673aafb5 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol @@ -0,0 +1,15 @@ +contract C { + function() internal view returns(uint256) immutable z; + constructor() public { + z = f; + } + function f() public view returns (uint256) { + return 7; + } + function callZ() public view returns (uint) { + return z(); + } +} +// ---- +// f() -> 7 +// callZ() -> 7 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol new file mode 100644 index 000000000..b9e362dbd --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -0,0 +1,31 @@ +contract A { + uint immutable a; + constructor() public { + a = 7; + } + function f() public view returns (uint) { return a; } +} +contract B { + uint immutable a; + constructor() public { + a = 5; + } + function f() public view returns (uint) { return a; } +} +contract C { + uint immutable a; + uint public x; + uint public y; + constructor() public { + a = 3; + x = (new A()).f(); + y = (new B()).f(); + } + function f() public returns (uint256, uint, uint) { + return (a, (new A()).f(), (new B()).f()); + } +} +// ---- +// f() -> 3, 7, 5 +// x() -> 7 +// y() -> 5 diff --git a/test/libsolidity/semanticTests/immutable/stub.sol b/test/libsolidity/semanticTests/immutable/stub.sol new file mode 100644 index 000000000..387541066 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/stub.sol @@ -0,0 +1,13 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + constructor() public { + x = 42; + y = 23; + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// f() -> 84, 23 diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol new file mode 100644 index 000000000..d83da476d --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -0,0 +1,19 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + mapping(uint => uint) public m; + constructor(uint _a) public { + x = 42; + y = 23; + m[_a] = 7; + new uint[](4); + + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// constructor(): 3 -> +// f() -> 84, 23 +// m(uint256): 3 -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol new file mode 100644 index 000000000..3d31c1add --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol @@ -0,0 +1,27 @@ +contract C { + function f() public { + assembly { + let d:= 0x10 + + function asmfun(a, b, c) - > x, y, z { + x := g(a) + function g(r) - > s { + s := mul(r, r) + } + y := g(b) + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + mstore(0x60, d) + return (0, 0x80) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1, 0x4, 0x7, 0x10 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol new file mode 100644 index 000000000..451ecbbe5 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol @@ -0,0 +1,26 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + function fac(n) -> nf { + nf := 1 + for { + let i := n + } gt(i, 0) { + i := sub(i, 1) + } { + nf := mul(nf, i) + } + } + b := fac(a) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 1 +// f(uint256): 1 -> 1 +// f(uint256): 2 -> 2 +// f(uint256): 3 -> 6 +// f(uint256): 4 -> 24 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol new file mode 100644 index 000000000..2c05f1556 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol @@ -0,0 +1,29 @@ +contract C { + uint256 st; + + function f(uint256 a) public returns (uint256 b, uint256 c, uint256 d) { + st = 0; + assembly { + function sideeffect(r) -> x { + sstore(0, add(sload(0), r)) + x := 1 + } + for { + let i := a + } eq(i, sideeffect(2)) { + d := add(d, 3) + } { + b := i + i := 0 + } + } + c = st; + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 0, 2, 0 +// f(uint256): 1 -> 1, 4, 3 +// f(uint256): 2 -> 0, 2, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol new file mode 100644 index 000000000..17a271d5b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol @@ -0,0 +1,21 @@ +contract C { + function f() public { + assembly { + function asmfun(a, b, c) - > x, y, z { + x := a + y := b + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + return (0, 0x60) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol new file mode 100644 index 000000000..26d3d43b7 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol @@ -0,0 +1,24 @@ +contract C { + function f() public { + assembly { + let d := 0x10 + + function asmfun(a, b, c) - > x, y, z { + x := a + y := b + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + mstore(0x60, d) + return (0, 0x80) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1, 0x2, 0x7, 0x10 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol new file mode 100644 index 000000000..2dec9761b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol @@ -0,0 +1,23 @@ +contract C { + function f() public { + assembly { + let a1, b1, c1 + + function asmfun(a, b, c) - > x, y, z { + x := a + y := b + z := 7 + } + a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + return (0, 0x60) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol new file mode 100644 index 000000000..9f94f3c19 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol @@ -0,0 +1,17 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + if gt(a, 1) { + b := 2 + } + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 0 +// f(uint256): 1 -> 0 +// f(uint256): 2 -> 2 +// f(uint256): 3 -> 2 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol new file mode 100644 index 000000000..3c9f3a2f3 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol @@ -0,0 +1,19 @@ +contract C { + modifier m { + uint256 a = 1; + assembly { + a := 2 + } + if (a != 2) revert(); + _; + } + + function f() public m returns (bool) { + return true; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol new file mode 100644 index 000000000..b43b5f611 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol @@ -0,0 +1,13 @@ +contract C { + function test() public returns (bytes memory) { + bytes memory x = new bytes(5); + for (uint256 i = 0; i < x.length; ++i) x[i] = bytes1(uint8(i + 1)); + assembly { + mstore(add(x, 32), "12345678901234567890123456789012") + } + return x; + } +} + +// ---- +// test() -> 0x20, 0x5, "12345" diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol new file mode 100644 index 000000000..c48d74967 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint256 r) { + for (uint256 x = 0; x < 10; ++x) + assembly { + r := add(r, x) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 45 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol new file mode 100644 index 000000000..a5e3c4675 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol @@ -0,0 +1,28 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + function fac(n) -> nf { + switch n + case 0 { + nf := 1 + } + case 1 { + nf := 1 + } + default { + nf := mul(n, fac(sub(n, 1))) + } + } + b := fac(a) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 1 +// f(uint256): 1 -> 1 +// f(uint256): 2 -> 2 +// f(uint256): 3 -> 6 +// f(uint256): 4 -> 24 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol new file mode 100644 index 000000000..823afe2bd --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol @@ -0,0 +1,22 @@ +contract C { + uint16 x; + uint16 public y; + uint256 public z; + + function f() public returns (bool) { + uint256 off1; + uint256 off2; + assembly { + sstore(z_slot, 7) + off1 := z_offset + off2 := y_offset + } + assert(off1 == 0); + assert(off2 == 2); + return true; + } +} + +// ---- +// f() -> true +// z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol new file mode 100644 index 000000000..ce56dbaa1 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol @@ -0,0 +1,23 @@ +contract C { + uint16 x; + uint16 public y; + uint256 public z; + + function f() public returns (bool) { + uint256 off1; + uint256 off2; + assembly { + function f() -> o1 { + sstore(z_slot, 7) + o1 := y_offset + } + off2 := f() + } + assert(off2 == 2); + return true; + } +} + +// ---- +// f() -> true +// z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol new file mode 100644 index 000000000..3893c8e89 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol @@ -0,0 +1,25 @@ +contract C { + struct Data { + uint256 contents; + } + uint256 public separator; + Data public a; + uint256 public separator2; + + function f() public returns (bool) { + Data storage x = a; + uint256 off; + assembly { + sstore(x_slot, 7) + off := x_offset + } + assert(off == 0); + return true; + } +} + +// ---- +// f() -> true +// a() -> 7 +// separator() -> 0 +// separator2() -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol new file mode 100644 index 000000000..9b9a76109 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol @@ -0,0 +1,24 @@ +contract C { + function f(uint256 a) public returns (uint256 b) { + assembly { + switch a + case 1 { + b := 8 + } + case 2 { + b := 9 + } + default { + b := 2 + } + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 2 +// f(uint256): 1 -> 8 +// f(uint256): 2 -> 9 +// f(uint256): 3 -> 2 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol new file mode 100644 index 000000000..fa981fbd3 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint256 r, bytes32 r2) { + assembly { + r := 7 + r2 := "abcdef" + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 7, "abcdef" diff --git a/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol b/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol new file mode 100644 index 000000000..250e0ce8a --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol @@ -0,0 +1,15 @@ +contract C { + function f() public pure returns (uint a, uint b) { + assembly { + let x + let y, z + a := x + b := z + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/keccak256_assembly.sol b/test/libsolidity/semanticTests/inlineAssembly/keccak256_assembly.sol new file mode 100644 index 000000000..d4034f10b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/keccak256_assembly.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure returns (bytes32 ret) { + assembly { + ret := keccak256(0, 0) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 diff --git a/test/libsolidity/semanticTests/intheritance/access_base_storage.sol b/test/libsolidity/semanticTests/intheritance/access_base_storage.sol new file mode 100644 index 000000000..a083ccd98 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/access_base_storage.sol @@ -0,0 +1,30 @@ +contract Base { + uint256 dataBase; + + function getViaBase() public returns (uint256 i) { + return dataBase; + } +} + + +contract Derived is Base { + uint256 dataDerived; + + function setData(uint256 base, uint256 derived) public returns (bool r) { + dataBase = base; + dataDerived = derived; + return true; + } + + function getViaDerived() public returns (uint256 base, uint256 derived) { + base = dataBase; + derived = dataDerived; + } +} + +// ==== +// compileViaYul: also +// ---- +// setData(uint256,uint256): 1, 2 -> true +// getViaBase() -> 1 +// getViaDerived() -> 1, 2 diff --git a/test/libsolidity/semanticTests/intheritance/address_overload_resolution.sol b/test/libsolidity/semanticTests/intheritance/address_overload_resolution.sol new file mode 100644 index 000000000..9b68cc154 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/address_overload_resolution.sol @@ -0,0 +1,24 @@ +contract C { + function balance() public returns (uint256) { + return 1; + } + + function transfer(uint256 amount) public returns (uint256) { + return amount; + } +} + + +contract D { + function f() public returns (uint256) { + return (new C()).balance(); + } + + function g() public returns (uint256) { + return (new C()).transfer(5); + } +} + +// ---- +// f() -> 1 +// g() -> 5 diff --git a/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol b/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol new file mode 100644 index 000000000..753e9bd1e --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol @@ -0,0 +1,21 @@ +contract C { + function() returns (uint256) internal x; + + function set() public { + C.x = g; + } + + function g() public pure returns (uint256) { + return 2; + } + + function h() public returns (uint256) { + return C.x(); + } +} + +// ---- +// g() -> 2 +// h() -> FAILURE +// set() -> +// h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol new file mode 100644 index 000000000..5628501d9 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol @@ -0,0 +1,21 @@ +contract B { + function f() public returns (uint256) { + return 10; + } +} + + +contract C is B { + function f(uint256 i) public returns (uint256) { + return 2 * i; + } + + function g() public returns (uint256) { + return f(1); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol new file mode 100644 index 000000000..949024f43 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol @@ -0,0 +1,29 @@ +contract A { + function f(uint256 a) public returns (uint256) { + return 2 * a; + } +} + + +contract B { + function f() public returns (uint256) { + return 10; + } +} + + +contract C is A, B { + function g() public returns (uint256) { + return f(); + } + + function h() public returns (uint256) { + return f(1); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 10 +// h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol b/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol new file mode 100644 index 000000000..44553b3f1 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol @@ -0,0 +1,27 @@ +contract BaseBase { + function g() public virtual returns (uint256 r) { + return 1; + } +} + + +contract Base is BaseBase { + function g() public virtual override returns (uint256 r) { + return 2; + } +} + + +contract Derived is Base { + function f() public returns (uint256 r) { + return BaseBase.g(); + } + + function g() public override returns (uint256 r) { + return 3; + } +} + +// ---- +// g() -> 3 +// f() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol new file mode 100644 index 000000000..e25db0dcb --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol @@ -0,0 +1,13 @@ +contract A { + uint256 constant x = 7; +} + + +contract B is A { + function f() public returns (uint256) { + return A.x; + } +} + +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function.sol b/test/libsolidity/semanticTests/intheritance/inherited_function.sol new file mode 100644 index 000000000..23f9bee37 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function.sol @@ -0,0 +1,19 @@ +contract A { + function f() internal virtual returns (uint256) { + return 1; + } +} + + +contract B is A { + function f() internal override returns (uint256) { + return 2; + } + + function g() public returns (uint256) { + return A.f(); + } +} + +// ---- +// g() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_calldata_interface.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_calldata_interface.sol new file mode 100644 index 000000000..9812ca520 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_calldata_interface.sol @@ -0,0 +1,25 @@ +interface I { + function f(uint256[] calldata a) external returns (uint256); +} + + +contract A is I { + function f(uint256[] calldata a) external override returns (uint256) { + return 42; + } +} + + +contract B { + function f(uint256[] memory a) public returns (uint256) { + return a[1]; + } + + function g() public returns (uint256) { + I i = I(new A()); + return i.f(new uint256[](2)); + } +} + +// ---- +// g() -> 42 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol new file mode 100644 index 000000000..0fd02e8f3 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory.sol @@ -0,0 +1,22 @@ +contract A { + function f(uint256[] calldata a) external virtual returns (uint256) { + return a[0]; + } +} + + +contract B is A { + function f(uint256[] memory a) public override returns (uint256) { + return a[1]; + } + + function g() public returns (uint256) { + uint256[] memory m = new uint256[](2); + m[0] = 42; + m[1] = 23; + return A(this).f(m); + } +} + +// ---- +// g() -> 23 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory_interface.sol new file mode 100644 index 000000000..9a2c1a1e7 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_calldata_memory_interface.sol @@ -0,0 +1,25 @@ +interface I { + function f(uint256[] calldata a) external returns (uint256); +} + + +contract A is I { + function f(uint256[] memory a) public override returns (uint256) { + return 42; + } +} + + +contract B { + function f(uint256[] memory a) public returns (uint256) { + return a[1]; + } + + function g() public returns (uint256) { + I i = I(new A()); + return i.f(new uint256[](2)); + } +} + +// ---- +// g() -> 42 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_from_a_library.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_from_a_library.sol new file mode 100644 index 000000000..42d4f711c --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_from_a_library.sol @@ -0,0 +1,19 @@ +library A { + function f() internal returns (uint256) { + return 1; + } +} + + +contract B { + function f() internal returns (uint256) { + return 2; + } + + function g() public returns (uint256) { + return A.f(); + } +} + +// ---- +// g() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol new file mode 100644 index 000000000..7f65eb4ad --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol @@ -0,0 +1,18 @@ +contract test { + function f(uint256 k) public returns (uint256 d) { + return k; + } + + function f(uint256 a, uint256 b) public returns (uint256 d) { + return a + b; + } + + function g() public returns (uint256 d) { + return f(3); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 3 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol new file mode 100644 index 000000000..bee0e8fa5 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol @@ -0,0 +1,18 @@ +contract test { + function f(uint256 a, uint256 b) public returns (uint256 d) { + return a + b; + } + + function f(uint256 k) public returns (uint256 d) { + return k; + } + + function g() public returns (uint256 d) { + return f(3, 7); + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol new file mode 100644 index 000000000..df437a32c --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol @@ -0,0 +1,20 @@ +contract test { + function f(uint256 a, uint256 b) public returns (uint256 d) { + return a + b; + } + + function f(uint256 k) public returns (uint256 d) { + return k; + } + + function g(bool flag) public returns (uint256 d) { + if (flag) return f(3); + else return f(3, 7); + } +} + +// ==== +// compileViaYul: also +// ---- +// g(bool): true -> 3 +// g(bool): false -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol new file mode 100644 index 000000000..26de9ce70 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base.sol @@ -0,0 +1,18 @@ +contract Base { + constructor(uint256 i) public { + m_i = i; + } + + uint256 public m_i; +} + + +contract Derived is Base { + constructor(uint256 i) public Base(i) {} +} + + +contract Final is Derived(4) {} + +// ---- +// m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol new file mode 100644 index 000000000..6349bd814 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base.sol @@ -0,0 +1,23 @@ +contract Base { + constructor(uint256 j) public { + m_i = j; + } + + uint256 public m_i; +} + + +contract Base1 is Base { + constructor(uint256 k) public Base(k) {} +} + + +contract Derived is Base, Base1 { + constructor(uint256 i) public Base1(i) {} +} + + +contract Final is Derived(4) {} + +// ---- +// m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol new file mode 100644 index 000000000..4556cf5c4 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/pass_dynamic_arguments_to_the_base_base_with_gap.sol @@ -0,0 +1,23 @@ +contract Base { + constructor(uint256 i) public { + m_i = i; + } + + uint256 public m_i; +} + + +abstract contract Base1 is Base { + constructor(uint256 k) public {} +} + + +contract Derived is Base, Base1 { + constructor(uint256 i) public Base(i) Base1(7) {} +} + + +contract Final is Derived(4) {} + +// ---- +// m_i() -> 4 diff --git a/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol new file mode 100644 index 000000000..33e270c9e --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol @@ -0,0 +1,35 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 4; + } +} + + +contract D is B, C { + uint256 data; + + constructor() public { + data = super.f() | 8; + } + + function f() public override (B, C) returns (uint256 r) { + return data; + } +} + +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/intheritance/super_overload.sol b/test/libsolidity/semanticTests/intheritance/super_overload.sol new file mode 100644 index 000000000..6a1f6dc3d --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/super_overload.sol @@ -0,0 +1,27 @@ +contract A { + function f(uint256 a) public returns (uint256) { + return 2 * a; + } +} + + +contract B { + function f(bool b) public returns (uint256) { + return 10; + } +} + + +contract C is A, B { + function g() public returns (uint256) { + return super.f(true); + } + + function h() public returns (uint256) { + return super.f(1); + } +} + +// ---- +// g() -> 10 +// h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/intheritance/value_for_constructor.sol new file mode 100644 index 000000000..5cd0bc177 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/value_for_constructor.sol @@ -0,0 +1,45 @@ +contract Helper { + bytes3 name; + bool flag; + + constructor(bytes3 x, bool f) public payable { + name = x; + flag = f; + } + + function getName() public returns (bytes3 ret) { + return name; + } + + function getFlag() public returns (bool ret) { + return flag; + } +} + + +contract Main { + Helper h; + + constructor() public payable { + h = (new Helper).value(10)("abc", true); + } + + function getFlag() public returns (bool ret) { + return h.getFlag(); + } + + function getName() public returns (bytes3 ret) { + return h.getName(); + } + + function getBalances() public returns (uint256 me, uint256 them) { + me = address(this).balance; + them = address(h).balance; + } +} + +// ---- +// constructor(), 22 wei -> +// getFlag() -> true +// getName() -> "abc" +// getBalances() -> 12, 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function.sol b/test/libsolidity/semanticTests/libraries/internal_library_function.sol new file mode 100644 index 000000000..a3c8a8700 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function.sol @@ -0,0 +1,21 @@ +// tests that internal library functions can be called from outside +// and retain the same memory context (i.e. are pulled into the caller's code) +// This has to work without linking, because everything will be inlined. +library L { + function f(uint256[] memory _data) internal { + _data[3] = 2; + } +} + + +contract C { + function f() public returns (uint256) { + uint256[] memory x = new uint256[](7); + x[3] = 8; + L.f(x); + return x[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound.sol new file mode 100644 index 000000000..f59ec9440 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound.sol @@ -0,0 +1,26 @@ +// This has to work without linking, because everything will be inlined. +library L { + struct S { + uint256[] data; + } + + function f(S memory _s) internal { + _s.data[3] = 2; + } +} + + +contract C { + using L for L.S; + + function f() public returns (uint256) { + L.S memory x; + x.data = new uint256[](7); + x.data[3] = 8; + x.f(); + return x.data[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_calling_private.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_calling_private.sol new file mode 100644 index 000000000..2283c30ff --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_calling_private.sol @@ -0,0 +1,26 @@ +// tests that internal library functions that are called from outside and that +// themselves call private functions are still able to (i.e. the private function +// also has to be pulled into the caller's code) +// This has to work without linking, because everything will be inlined. +library L { + function g(uint256[] memory _data) private { + _data[3] = 2; + } + + function f(uint256[] memory _data) internal { + g(_data); + } +} + + +contract C { + function f() public returns (uint256) { + uint256[] memory x = new uint256[](7); + x[3] = 8; + L.f(x); + return x[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_return_var_size.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_return_var_size.sol new file mode 100644 index 000000000..21417b599 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_return_var_size.sol @@ -0,0 +1,26 @@ +// This has to work without linking, because everything will be inlined. +library L { + struct S { + uint256[] data; + } + + function f(S memory _s) internal returns (uint256[] memory) { + _s.data[3] = 2; + return _s.data; + } +} + + +contract C { + using L for L.S; + + function f() public returns (uint256) { + L.S memory x; + x.data = new uint256[](7); + x.data[3] = 8; + return x.f()[3]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol b/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol new file mode 100644 index 000000000..f24d93c2d --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_enum_as_an_expression.sol @@ -0,0 +1,14 @@ +library Arst { + enum Foo {Things, Stuff} +} + + +contract Tsra { + function f() public returns (uint256) { + Arst.Foo; + return 1; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol b/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol new file mode 100644 index 000000000..d7df52434 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_struct_as_an_expression.sol @@ -0,0 +1,17 @@ +library Arst { + struct Foo { + int256 Things; + int256 Stuff; + } +} + + +contract Tsra { + function f() public returns (uint256) { + Arst.Foo; + return 1; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/literals/scientific_notation.sol b/test/libsolidity/semanticTests/literals/scientific_notation.sol new file mode 100644 index 000000000..e79fca70b --- /dev/null +++ b/test/libsolidity/semanticTests/literals/scientific_notation.sol @@ -0,0 +1,36 @@ +contract C { + function f() public returns(uint) { + return 2e10 wei; + } + + function g() public returns(uint) { + return 200e-2 wei; + } + + function h() public returns(uint) { + return 2.5e1; + } + + function i() public returns(int) { + return -2e10; + } + + function j() public returns(int) { + return -200e-2; + } + + function k() public returns(int) { + return -2.5e1; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 20000000000 +// g() -> 2 +// h() -> 25 +// i() -> -20000000000 +// j() -> -2 +// k() -> -25 + diff --git a/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol new file mode 100644 index 000000000..1d1036c2c --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 0; i < 10; i++) { + _; + break; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 1 diff --git a/test/libsolidity/semanticTests/modifiers/continue_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/continue_in_modifier.sol new file mode 100644 index 000000000..1db0f2e9f --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/continue_in_modifier.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 0; i < 10; i++) { + if (i % 2 == 1) continue; + _; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 5 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier.sol b/test/libsolidity/semanticTests/modifiers/function_modifier.sol new file mode 100644 index 000000000..fcb8f64a2 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier.sol @@ -0,0 +1,13 @@ +contract C { + function getOne() public payable nonFree returns (uint256 r) { + return 1; + } + + modifier nonFree { + if (msg.value > 0) _; + } +} + +// ---- +// getOne() -> 0 +// getOne(), 1 wei -> 1 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_calling_functions_in_creation_context.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_calling_functions_in_creation_context.sol new file mode 100644 index 000000000..f4a73ebeb --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_calling_functions_in_creation_context.sol @@ -0,0 +1,49 @@ +contract A { + uint256 data; + + constructor() public mod1 { + f1(); + } + + function f1() public mod2 { + data |= 0x1; + } + + function f2() public { + data |= 0x20; + } + + function f3() public virtual {} + + modifier mod1 virtual { + f2(); + _; + } + modifier mod2 { + f3(); + if (false) _; + } + + function getData() public returns (uint256 r) { + return data; + } +} + + +contract C is A { + modifier mod1 override { + f4(); + _; + } + + function f3() public override { + data |= 0x300; + } + + function f4() public { + data |= 0x4000; + } +} + +// ---- +// getData() -> 0x4300 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_for_constructor.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_for_constructor.sol new file mode 100644 index 000000000..ac99b0cf7 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_for_constructor.sol @@ -0,0 +1,27 @@ +contract A { + uint256 data; + + constructor() public mod1 { + data |= 2; + } + + modifier mod1 virtual { + data |= 1; + _; + } + + function getData() public returns (uint256 r) { + return data; + } +} + + +contract C is A { + modifier mod1 override { + data |= 4; + _; + } +} + +// ---- +// getData() -> 6 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_library.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_library.sol new file mode 100644 index 000000000..f10ebb0e7 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_library.sol @@ -0,0 +1,28 @@ +library L { + struct S { + uint256 v; + } + modifier mod(S storage s) { + s.v++; + _; + } + + function libFun(S storage s) internal mod(s) { + s.v += 0x100; + } +} + + +contract Test { + using L for *; + L.S s; + + function f() public returns (uint256) { + s.libFun(); + L.libFun(s); + return s.v; + } +} + +// ---- +// f() -> 0x202 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_library_inheritance.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_library_inheritance.sol new file mode 100644 index 000000000..3d5e97de0 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_library_inheritance.sol @@ -0,0 +1,34 @@ +// Tests that virtual lookup for modifiers in libraries does not consider +// the current inheritance hierarchy. +library L { + struct S { + uint256 v; + } + modifier mod(S storage s) { + s.v++; + _; + } + + function libFun(S storage s) internal mod(s) { + s.v += 0x100; + } +} + + +contract Test { + using L for *; + L.S s; + modifier mod(L.S storage) { + revert(); + _; + } + + function f() public returns (uint256) { + s.libFun(); + L.libFun(s); + return s.v; + } +} + +// ---- +// f() -> 0x202 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_local_variables.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_local_variables.sol new file mode 100644 index 000000000..1df8b0874 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_local_variables.sol @@ -0,0 +1,19 @@ +contract C { + modifier mod1 { + uint8 a = 1; + uint8 b = 2; + _; + } + modifier mod2(bool a) { + if (a) return; + else _; + } + + function f(bool a) public mod1 mod2(a) returns (uint256 r) { + return 3; + } +} + +// ---- +// f(bool): true -> 0 +// f(bool): false -> 3 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_loop.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_loop.sol new file mode 100644 index 000000000..0c4c07828 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_loop.sol @@ -0,0 +1,13 @@ +contract C { + modifier repeat(uint256 count) { + uint256 i; + for (i = 0; i < count; ++i) _; + } + + function f() public repeat(10) returns (uint256 r) { + r += 1; + } +} + +// ---- +// f() -> 10 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multi_invocation.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_invocation.sol new file mode 100644 index 000000000..124e15970 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_invocation.sol @@ -0,0 +1,14 @@ +contract C { + modifier repeat(bool twice) { + if (twice) _; + _; + } + + function f(bool twice) public repeat(twice) returns (uint256 r) { + r += 1; + } +} + +// ---- +// f(bool): false -> 1 +// f(bool): true -> 2 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multi_with_return.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_with_return.sol new file mode 100644 index 000000000..286476820 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multi_with_return.sol @@ -0,0 +1,17 @@ +// Note that return sets the return variable and jumps to the end of the current function or +// modifier code block. +contract C { + modifier repeat(bool twice) { + if (twice) _; + _; + } + + function f(bool twice) public repeat(twice) returns (uint256 r) { + r += 1; + return r; + } +} + +// ---- +// f(bool): false -> 1 +// f(bool): true -> 2 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times.sol new file mode 100644 index 000000000..722334ec9 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times.sol @@ -0,0 +1,15 @@ +contract C { + uint256 public a; + modifier mod(uint256 x) { + a += x; + _; + } + + function f(uint256 x) public mod(2) mod(5) mod(x) returns (uint256) { + return a; + } +} + +// ---- +// f(uint256): 3 -> 10 +// a() -> 10 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times_local_vars.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times_local_vars.sol new file mode 100644 index 000000000..64354ba5e --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_multiple_times_local_vars.sol @@ -0,0 +1,18 @@ +contract C { + uint256 public a; + modifier mod(uint256 x) { + uint256 b = x; + a += b; + _; + a -= b; + assert(b == x); + } + + function f(uint256 x) public mod(2) mod(5) mod(x) returns (uint256) { + return a; + } +} + +// ---- +// f(uint256): 3 -> 10 +// a() -> 0 diff --git a/test/libsolidity/semanticTests/modifiers/function_modifier_overriding.sol b/test/libsolidity/semanticTests/modifiers/function_modifier_overriding.sol new file mode 100644 index 000000000..c91869615 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/function_modifier_overriding.sol @@ -0,0 +1,19 @@ +contract A { + function f() public mod returns (bool r) { + return true; + } + + modifier mod virtual { + _; + } +} + + +contract C is A { + modifier mod override { + if (false) _; + } +} + +// ---- +// f() -> false diff --git a/test/libsolidity/semanticTests/modifiers/return_does_not_skip_modifier.sol b/test/libsolidity/semanticTests/modifiers/return_does_not_skip_modifier.sol new file mode 100644 index 000000000..c437922e4 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/return_does_not_skip_modifier.sol @@ -0,0 +1,16 @@ +contract C { + uint256 public x; + modifier setsx { + _; + x = 9; + } + + function f() public setsx returns (uint256) { + return 2; + } +} + +// ---- +// x() -> 0 +// f() -> 2 +// x() -> 9 diff --git a/test/libsolidity/semanticTests/modifiers/return_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/return_in_modifier.sol new file mode 100644 index 000000000..81fbd794e --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/return_in_modifier.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 1; i < 10; i++) { + if (i == 5) return; + _; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 4 diff --git a/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol new file mode 100644 index 000000000..1d1036c2c --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol @@ -0,0 +1,20 @@ +contract C { + uint256 public x; + modifier run() { + for (uint256 i = 0; i < 10; i++) { + _; + break; + } + } + + function f() public run { + uint256 k = x; + uint256 t = k + 1; + x = t; + } +} + +// ---- +// x() -> 0 +// f() -> +// x() -> 1 diff --git a/test/libsolidity/semanticTests/reverts/assert_require.sol b/test/libsolidity/semanticTests/reverts/assert_require.sol new file mode 100644 index 000000000..6bd146a7a --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/assert_require.sol @@ -0,0 +1,24 @@ +contract C { + function f() public { + assert(false); + } + + function g(bool val) public returns (bool) { + assert(val == true); + return true; + } + + function h(bool val) public returns (bool) { + require(val); + return true; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE +// g(bool): false -> FAILURE +// g(bool): true -> true +// h(bool): false -> FAILURE +// h(bool): true -> true diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol new file mode 100644 index 000000000..d0adf2785 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol @@ -0,0 +1,20 @@ +contract C { + enum X {A, B} + + function tested(X x) public returns (uint256) { + return 1; + } + + function test() public returns (uint256) { + X garbled; + + assembly { + garbled := 5 + } + + return this.tested(garbled); + } +} + +// ---- +// test() -> FAILURE # should throw # diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol new file mode 100644 index 000000000..6bdf14298 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol @@ -0,0 +1,32 @@ +contract C { + enum X {A, B} + + function test_return() public returns (X) { + X garbled; + assembly { + garbled := 5 + } + return garbled; + } + + function test_inline_assignment() public returns (X _ret) { + assembly { + _ret := 5 + } + } + + function test_assignment() public returns (X _ret) { + X tmp; + assembly { + tmp := 5 + } + _ret = tmp; + } +} + +// ==== +// compileViaYul: also +// ---- +// test_return() -> FAILURE # both should throw # +// test_inline_assignment() -> FAILURE +// test_assignment() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol new file mode 100644 index 000000000..17ee32bb8 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol @@ -0,0 +1,29 @@ +contract C { + enum X {A, B} + + function test_eq() public returns (bool) { + X garbled; + assembly { + garbled := 5 + } + return garbled == garbled; + } + + function test_eq_ok() public returns (bool) { + X garbled = X.A; + return garbled == garbled; + } + + function test_neq() public returns (bool) { + X garbled; + assembly { + garbled := 5 + } + return garbled != garbled; + } +} + +// ---- +// test_eq_ok() -> 1 +// test_eq() -> FAILURE # both should throw # +// test_neq() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol new file mode 100644 index 000000000..2b90451ec --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol @@ -0,0 +1,23 @@ +contract C { + enum X {A, B} + X public x; + + function test_store() public returns (uint256) { + X garbled = X.A; + assembly { + garbled := 5 + } + x = garbled; + return 1; + } + + function test_store_ok() public returns (uint256) { + x = X.A; + return 1; + } +} + +// ---- +// test_store_ok() -> 1 +// x() -> 0 +// test_store() -> FAILURE # should throw # diff --git a/test/libsolidity/semanticTests/reverts/invalid_instruction.sol b/test/libsolidity/semanticTests/reverts/invalid_instruction.sol new file mode 100644 index 000000000..839296ca1 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/invalid_instruction.sol @@ -0,0 +1,12 @@ +contract C { + function f() public { + assembly { + invalid() + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/revert.sol b/test/libsolidity/semanticTests/reverts/revert.sol new file mode 100644 index 000000000..eae49c799 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/revert.sol @@ -0,0 +1,21 @@ +contract C { + uint256 public a = 42; + + function f() public { + a = 1; + revert(); + } + + function g() public { + a = 1; + assembly { + revert(0, 0) + } + } +} + +// ---- +// f() -> FAILURE +// a() -> 42 +// g() -> FAILURE +// a() -> 42 diff --git a/test/libsolidity/semanticTests/reverts/simple_throw.sol b/test/libsolidity/semanticTests/reverts/simple_throw.sol new file mode 100644 index 000000000..ad88deca6 --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/simple_throw.sol @@ -0,0 +1,11 @@ +contract Test { + function f(uint256 x) public returns (uint256) { + if (x > 10) return x + 10; + else revert(); + return 2; + } +} + +// ---- +// f(uint256): 11 -> 21 +// f(uint256): 1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol new file mode 100644 index 000000000..c6ada8c6a --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol @@ -0,0 +1,34 @@ +contract C { + function shl(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := shl(b, a) + } + } + + function shr(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := shr(b, a) + } + } + + function sar(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := sar(b, a) + } + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl(uint256,uint256): 0x01, 0x02 -> 0x04 +// shl(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// shl(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0x00 +// shr(uint256,uint256): 0x03, 0x01 -> 0x01 +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xff -> 0x01 +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0x00 +// sar(uint256,uint256): 0x03, 0x01 -> 0x01 +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol new file mode 100644 index 000000000..6cacbe300 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol @@ -0,0 +1,122 @@ +contract C { + function shl_zero(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0, a) + } + } + + function shr_zero(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0, a) + } + } + + function sar_zero(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0, a) + } + } + + function shl_large(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0x110, a) + } + } + + function shr_large(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0x110, a) + } + } + + function sar_large(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0x110, a) + } + } + + function shl_combined(uint256 a) public returns (uint256 c) { + assembly { + c := shl(4, shl(12, a)) + } + } + + function shr_combined(uint256 a) public returns (uint256 c) { + assembly { + c := shr(4, shr(12, a)) + } + } + + function sar_combined(uint256 a) public returns (uint256 c) { + assembly { + c := sar(4, sar(12, a)) + } + } + + function shl_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0xd0, shl(0x40, a)) + } + } + + function shl_combined_overflow(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0x01, shl(not(0x00), a)) + } + } + + function shr_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0xd0, shr(0x40, a)) + } + } + + function shr_combined_overflow(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0x01, shr(not(0x00), a)) + } + } + + function sar_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0xd0, sar(0x40, a)) + } + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl_zero(uint256): 0x00 -> 0x00 +// shl_zero(uint256): 0xffff -> 0xffff +// shl_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shr_zero(uint256): 0x00 -> 0x00 +// shr_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_zero(uint256): 0x00 -> 0x00 +// sar_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_large(uint256): 0x00 -> 0x00 +// shl_large(uint256): 0xffff -> 0x00 +// shl_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shr_large(uint256): 0x00 -> 0x00 +// shr_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_large(uint256): 0x00 -> 0x00 +// sar_large(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_combined(uint256): 0x00 -> 0x00 +// shl_combined(uint256): 0xffff -> 0xffff0000 +// shl_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000 +// shr_combined(uint256): 0x00 -> 0x00 +// shr_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_combined(uint256): 0x00 -> 0x00 +// sar_combined(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_combined_large(uint256): 0x00 -> 0x00 +// shl_combined_large(uint256): 0xffff -> 0x00 +// shl_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shl_combined_overflow(uint256): 0x02 -> 0x00 +// shr_combined_large(uint256): 0x00 -> 0x00 +// shr_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shr_combined_overflow(uint256): 0x02 -> 0x00 +// sar_combined_large(uint256): 0x00 -> 0x00 +// sar_combined_large(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol new file mode 100644 index 000000000..bbc5ddaa4 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol @@ -0,0 +1,83 @@ +contract C { + function shl_1() public returns (bool) { + uint256 c; + assembly { + c := shl(2, 1) + } + assert(c == 4); + return true; + } + + function shl_2() public returns (bool) { + uint256 c; + assembly { + c := shl( + 1, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert( + c == + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + ); + return true; + } + + function shl_3() public returns (bool) { + uint256 c; + assembly { + c := shl( + 256, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert(c == 0); + return true; + } + + function shr_1() public returns (bool) { + uint256 c; + assembly { + c := shr(1, 3) + } + assert(c == 1); + return true; + } + + function shr_2() public returns (bool) { + uint256 c; + assembly { + c := shr( + 1, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert( + c == + 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ); + return true; + } + + function shr_3() public returns (bool) { + uint256 c; + assembly { + c := shr( + 256, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert(c == 0); + return true; + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl_1() -> 0x01 +// shl_2() -> 0x01 +// shl_3() -> 0x01 +// shr_1() -> 0x01 +// shr_2() -> 0x01 +// shr_3() -> 0x01 diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup.sol b/test/libsolidity/semanticTests/shifts/shift_cleanup.sol new file mode 100644 index 000000000..81296ba95 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_cleanup.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint16 x) { + x = 0xffff; + x += 32; + x <<= 8; + x >>= 16; + } +} + +// ---- +// f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol b/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol new file mode 100644 index 000000000..cc81c15e4 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint8 x) { + assembly { + x := 0xffff + } + x >>= 8; + } +} + +// ---- +// f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left.sol b/test/libsolidity/semanticTests/shifts/shift_constant_left.sol new file mode 100644 index 000000000..4c6f3737f --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_left.sol @@ -0,0 +1,6 @@ +contract C { + uint256 public a = 0x42 << 8; +} + +// ---- +// a() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol new file mode 100644 index 000000000..e5a4152b5 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint256 a) { + a = 0x42; + a <<= 8; + } +} + +// ---- +// f() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right.sol b/test/libsolidity/semanticTests/shifts/shift_constant_right.sol new file mode 100644 index 000000000..766a8522e --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_right.sol @@ -0,0 +1,6 @@ +contract C { + uint256 public a = 0x4200 >> 8; +} + +// ---- +// a() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol new file mode 100644 index 000000000..0f36c10ee --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (uint256 a) { + a = 0x4200; + a >>= 8; + } +} + +// ---- +// f() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_left.sol b/test/libsolidity/semanticTests/shifts/shift_left.sol new file mode 100644 index 000000000..15d2a972a --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + return a << b; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x426600 +// f(uint256,uint256): 0x4266, 0x10 -> 0x42660000 +// f(uint256,uint256): 0x4266, 0x11 -> 0x84cc0000 +// f(uint256,uint256): 0x4266, 0xf0 -> 0x4266000000000000000000000000000000000000000000000000000000000000 +// f(uint256,uint256): 0x4266, 0x100 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol new file mode 100644 index 000000000..06cb38606 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + a <<= b; + return a; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x426600 +// f(uint256,uint256): 0x4266, 0x10 -> 0x42660000 +// f(uint256,uint256): 0x4266, 0x11 -> 0x84cc0000 +// f(uint256,uint256): 0x4266, 0xf0 -> 0x4266000000000000000000000000000000000000000000000000000000000000 +// f(uint256,uint256): 0x4266, 0x100 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol b/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol new file mode 100644 index 000000000..5cc15c1a8 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint256 a, uint8 b) public returns (uint256) { + a <<= b; + return a; + } +} + +// ---- +// f(uint256,uint8): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint8): 0x4266, 0x8 -> 0x426600 +// f(uint256,uint8): 0x4266, 0x10 -> 0x42660000 +// f(uint256,uint8): 0x4266, 0x11 -> 0x84cc0000 +// f(uint256,uint8): 0x4266, 0xf0 -> 0x4266000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol b/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol new file mode 100644 index 000000000..99ff376d6 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol @@ -0,0 +1,11 @@ +// This basically tests proper cleanup and conversion. It should not convert x to int8. +contract C { + function f() public returns (int8) { + uint8 x = 254; + int8 y = 1; + return y << x; + } +} + +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol b/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol new file mode 100644 index 000000000..0f35077b1 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint32 a, uint32 b) public returns (uint256) { + return a << b; + } +} + +// ---- +// f(uint32,uint32): 0x4266, 0x0 -> 0x4266 +// f(uint32,uint32): 0x4266, 0x8 -> 0x426600 +// f(uint32,uint32): 0x4266, 0x10 -> 0x42660000 +// f(uint32,uint32): 0x4266, 0x11 -> 0x84cc0000 +// f(uint32,uint32): 0x4266, 0x20 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol b/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol new file mode 100644 index 000000000..3070314f8 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + return a << b; + } +} + +// ---- +// f(uint8,uint8): 0x66, 0x0 -> 0x66 +// f(uint8,uint8): 0x66, 0x8 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol b/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol new file mode 100644 index 000000000..b92fb2229 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol @@ -0,0 +1,6 @@ +contract C { + int256 public a = -0x42 << 8; +} + +// ---- +// a() -> -16896 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol b/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol new file mode 100644 index 000000000..b08463333 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol @@ -0,0 +1,6 @@ +contract C { + int256 public a = -0x4200 >> 8; +} + +// ---- +// a() -> -66 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol new file mode 100644 index 000000000..77c18c44b --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue.sol @@ -0,0 +1,13 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + return a << b; + } + + function g(int256 a, int256 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int256,int256): 1, -1 -> FAILURE +// g(int256,int256): 1, -1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol new file mode 100644 index 000000000..e63a9a57e --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_negative_rvalue_assignment.sol @@ -0,0 +1,15 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + a <<= b; + return a; + } + + function g(int256 a, int256 b) public returns (int256) { + a >>= b; + return a; + } +} + +// ---- +// f(int256,int256): 1, -1 -> FAILURE +// g(int256,int256): 1, -1 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_overflow.sol b/test/libsolidity/semanticTests/shifts/shift_overflow.sol new file mode 100644 index 000000000..f1b4bca0a --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_overflow.sol @@ -0,0 +1,16 @@ +contract C { + function leftU(uint8 x, uint8 y) public returns (uint8) { + return x << y; + } + + function leftS(int8 x, int8 y) public returns (int8) { + return x << y; + } +} + +// ---- +// leftU(uint8,uint8): 255, 8 -> 0 +// leftU(uint8,uint8): 255, 1 -> 254 +// leftU(uint8,uint8): 255, 0 -> 255 +// leftS(int8,int8): 1, 7 -> -128 # Result is -128 and output is sign-extended, not zero-padded. # +// leftS(int8,int8): 1, 6 -> 64 diff --git a/test/libsolidity/semanticTests/shifts/shift_right.sol b/test/libsolidity/semanticTests/shifts/shift_right.sol new file mode 100644 index 000000000..d78d18aba --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + return a >> b; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x42 +// f(uint256,uint256): 0x4266, 0x10 -> 0 +// f(uint256,uint256): 0x4266, 0x11 -> 0 +// f(uint256,uint256): 57896044618658097711785492504343953926634992332820282019728792003956564819968, 5 -> 1809251394333065553493296640760748560207343510400633813116524750123642650624 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol new file mode 100644 index 000000000..cfee67301 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 a, uint256 b) public returns (uint256) { + a >>= b; + return a; + } +} + +// ---- +// f(uint256,uint256): 0x4266, 0x0 -> 0x4266 +// f(uint256,uint256): 0x4266, 0x8 -> 0x42 +// f(uint256,uint256): 0x4266, 0x10 -> 0 +// f(uint256,uint256): 0x4266, 0x11 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol b/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol new file mode 100644 index 000000000..ba819fbce --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_assignment_signed.sol @@ -0,0 +1,12 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + a >>= b; + return a; + } +} + +// ---- +// f(int256,int256): 0x4266, 0x0 -> 0x4266 +// f(int256,int256): 0x4266, 0x8 -> 0x42 +// f(int256,int256): 0x4266, 0x10 -> 0 +// f(int256,int256): 0x4266, 0x11 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol new file mode 100644 index 000000000..c0d6be8bd --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + assembly { + a := 0xffffffff + } + // Higher bits should be cleared before the shift + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(uint8,uint8): 0x00, 0x04 -> 0x0f +// f(uint8,uint8): 0x00, 0x1004 -> 0x0f diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol new file mode 100644 index 000000000..df0570cb7 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol @@ -0,0 +1,30 @@ +contract C { + function f(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + + function g(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0x1003 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x1004 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x1003 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x1004 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol new file mode 100644 index 000000000..0c1949a59 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol @@ -0,0 +1,31 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + + function g(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } +} +// ---- +// f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0x1003 -> FAILURE +// f(int8,uint8): 0x00, 0x1004 -> FAILURE +// g(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x1003 -> FAILURE +// g(int8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol new file mode 100644 index 000000000..54ac9540f --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + assembly { + a := 0xffffffff + } + // Higher bits should be cleared before the shift + return a >> b; + } +} +// ---- +// f(uint8,uint8): 0x00, 0x04 -> 0x0f +// f(uint8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol new file mode 100644 index 000000000..2ae9647e2 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol @@ -0,0 +1,65 @@ +contract C { + function f1() public pure returns (bool) { + return (-4266 >> 0) == -4266; + } + + function f2() public pure returns (bool) { + return (-4266 >> 1) == -2133; + } + + function f3() public pure returns (bool) { + return (-4266 >> 4) == -267; + } + + function f4() public pure returns (bool) { + return (-4266 >> 8) == -17; + } + + function f5() public pure returns (bool) { + return (-4266 >> 16) == -1; + } + + function f6() public pure returns (bool) { + return (-4266 >> 17) == -1; + } + + function g1() public pure returns (bool) { + return (-4267 >> 0) == -4267; + } + + function g2() public pure returns (bool) { + return (-4267 >> 1) == -2134; + } + + function g3() public pure returns (bool) { + return (-4267 >> 4) == -267; + } + + function g4() public pure returns (bool) { + return (-4267 >> 8) == -17; + } + + function g5() public pure returns (bool) { + return (-4267 >> 16) == -1; + } + + function g6() public pure returns (bool) { + return (-4267 >> 17) == -1; + } +} + +// ==== +// compileViaYul: also +// ---- +// f1() -> true +// f2() -> true +// f3() -> true +// f4() -> true +// f5() -> true +// f6() -> true +// g1() -> true +// g2() -> true +// g3() -> true +// g4() -> true +// g5() -> true +// g6() -> true diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol new file mode 100644 index 000000000..73aae5bc9 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol @@ -0,0 +1,19 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int256,int256): -4266, 0 -> -4266 +// f(int256,int256): -4266, 1 -> -2133 +// f(int256,int256): -4266, 4 -> -267 +// f(int256,int256): -4266, 8 -> -17 +// f(int256,int256): -4266, 16 -> -1 +// f(int256,int256): -4266, 17 -> -1 +// f(int256,int256): -4267, 0 -> -4267 +// f(int256,int256): -4267, 1 -> -2134 +// f(int256,int256): -4267, 4 -> -267 +// f(int256,int256): -4267, 8 -> -17 +// f(int256,int256): -4267, 16 -> -1 +// f(int256,int256): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol new file mode 100644 index 000000000..7f3beb59f --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol @@ -0,0 +1,20 @@ +contract C { + function f(int256 a, int256 b) public returns (int256) { + a >>= b; + return a; + } +} + +// ---- +// f(int256,int256): -4266, 0 -> -4266 +// f(int256,int256): -4266, 1 -> -2133 +// f(int256,int256): -4266, 4 -> -267 +// f(int256,int256): -4266, 8 -> -17 +// f(int256,int256): -4266, 16 -> -1 +// f(int256,int256): -4266, 17 -> -1 +// f(int256,int256): -4267, 0 -> -4267 +// f(int256,int256): -4267, 1 -> -2134 +// f(int256,int256): -4267, 4 -> -267 +// f(int256,int256): -4267, 8 -> -17 +// f(int256,int256): -4267, 16 -> -1 +// f(int256,int256): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol new file mode 100644 index 000000000..24ab54123 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol @@ -0,0 +1,19 @@ +contract C { + function f(int16 a, int16 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int16,int16): -4266, 0 -> -4266 +// f(int16,int16): -4266, 1 -> -2133 +// f(int16,int16): -4266, 4 -> -267 +// f(int16,int16): -4266, 8 -> -17 +// f(int16,int16): -4266, 16 -> -1 +// f(int16,int16): -4266, 17 -> -1 +// f(int16,int16): -4267, 0 -> -4267 +// f(int16,int16): -4267, 1 -> -2134 +// f(int16,int16): -4267, 4 -> -267 +// f(int16,int16): -4267, 8 -> -17 +// f(int16,int16): -4267, 16 -> -1 +// f(int16,int16): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol new file mode 100644 index 000000000..7ff669dc9 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol @@ -0,0 +1,19 @@ +contract C { + function f(int32 a, int32 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int32,int32): -4266, 0 -> -4266 +// f(int32,int32): -4266, 1 -> -2133 +// f(int32,int32): -4266, 4 -> -267 +// f(int32,int32): -4266, 8 -> -17 +// f(int32,int32): -4266, 16 -> -1 +// f(int32,int32): -4266, 17 -> -1 +// f(int32,int32): -4267, 0 -> -4267 +// f(int32,int32): -4267, 1 -> -2134 +// f(int32,int32): -4267, 4 -> -267 +// f(int32,int32): -4267, 8 -> -17 +// f(int32,int32): -4267, 16 -> -1 +// f(int32,int32): -4267, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol new file mode 100644 index 000000000..c6424f141 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol @@ -0,0 +1,19 @@ +contract C { + function f(int8 a, int8 b) public returns (int256) { + return a >> b; + } +} + +// ---- +// f(int8,int8): -66, 0 -> -66 +// f(int8,int8): -66, 1 -> -33 +// f(int8,int8): -66, 4 -> -5 +// f(int8,int8): -66, 8 -> -1 +// f(int8,int8): -66, 16 -> -1 +// f(int8,int8): -66, 17 -> -1 +// f(int8,int8): -67, 0 -> -67 +// f(int8,int8): -67, 1 -> -34 +// f(int8,int8): -67, 4 -> -5 +// f(int8,int8): -67, 8 -> -1 +// f(int8,int8): -67, 16 -> -1 +// f(int8,int8): -67, 17 -> -1 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol new file mode 100644 index 000000000..c59107f52 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol @@ -0,0 +1,13 @@ +contract C { + function f(int16 a, int16 b) public returns (int16) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int16,int16): 0xff99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int16,int16): 0xff99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int16,int16): 0xff99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int16,int16): 0xff99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int16,int16): 0xff99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol new file mode 100644 index 000000000..6e462e704 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int16 a, int16 b) public returns (int16) { + return a >> b; + } +} +// ---- +// f(int16,int16): 0xff99, 0x00 -> FAILURE +// f(int16,int16): 0xff99, 0x01 -> FAILURE +// f(int16,int16): 0xff99, 0x02 -> FAILURE +// f(int16,int16): 0xff99, 0x04 -> FAILURE +// f(int16,int16): 0xff99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol new file mode 100644 index 000000000..74e9d53b0 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol @@ -0,0 +1,13 @@ +contract C { + function f(int32 a, int32 b) public returns (int32) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int32,int32): 0xffffff99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int32,int32): 0xffffff99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int32,int32): 0xffffff99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int32,int32): 0xffffff99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int32,int32): 0xffffff99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol new file mode 100644 index 000000000..2466298f0 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int32 a, int32 b) public returns (int32) { + return a >> b; + } +} +// ---- +// f(int32,int32): 0xffffff99, 0x00 -> FAILURE +// f(int32,int32): 0xffffff99, 0x01 -> FAILURE +// f(int32,int32): 0xffffff99, 0x02 -> FAILURE +// f(int32,int32): 0xffffff99, 0x04 -> FAILURE +// f(int32,int32): 0xffffff99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol new file mode 100644 index 000000000..06dcf8eb5 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol @@ -0,0 +1,13 @@ +contract C { + function f(int8 a, int8 b) public returns (int8) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int8,int8): 0x99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int8,int8): 0x99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int8,int8): 0x99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int8,int8): 0x99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int8,int8): 0x99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol new file mode 100644 index 000000000..643bc5e62 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int8 a, int8 b) public returns (int8) { + return a >> b; + } +} +// ---- +// f(int8,int8): 0x99, 0x00 -> FAILURE +// f(int8,int8): 0x99, 0x01 -> FAILURE +// f(int8,int8): 0x99, 0x02 -> FAILURE +// f(int8,int8): 0x99, 0x04 -> FAILURE +// f(int8,int8): 0x99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol b/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol new file mode 100644 index 000000000..03573d985 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol @@ -0,0 +1,11 @@ +contract C { + function f(uint32 a, uint32 b) public returns (uint256) { + return a >> b; + } +} + +// ---- +// f(uint32,uint32): 0x4266, 0x0 -> 0x4266 +// f(uint32,uint32): 0x4266, 0x8 -> 0x42 +// f(uint32,uint32): 0x4266, 0x10 -> 0 +// f(uint32,uint32): 0x4266, 0x11 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol b/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol new file mode 100644 index 000000000..8457b10e1 --- /dev/null +++ b/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + return a >> b; + } +} + +// ---- +// f(uint8,uint8): 0x66, 0x0 -> 0x66 +// f(uint8,uint8): 0x66, 0x8 -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts.sol b/test/libsolidity/semanticTests/shifts/shifts.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts.sol rename to test/libsolidity/semanticTests/shifts/shifts.sol diff --git a/test/libsolidity/semanticTests/storage/packed_functions.sol b/test/libsolidity/semanticTests/storage/packed_functions.sol new file mode 100644 index 000000000..4a49a614f --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_functions.sol @@ -0,0 +1,48 @@ +contract C { + // these should take the same slot + function() internal returns (uint) a; + function() external returns (uint) b; + function() external returns (uint) c; + function() internal returns (uint) d; + uint8 public x; + + function set() public { + x = 2; + d = g; + c = this.h; + b = this.h; + a = g; + } + + function t1() public returns (uint256) { + return a(); + } + + function t2() public returns (uint256) { + return b(); + } + + function t3() public returns (uint256) { + return a(); + } + + function t4() public returns (uint256) { + return b(); + } + + function g() public returns (uint256) { + return 7; + } + + function h() public returns (uint256) { + return 8; + } +} + +// ---- +// set() -> +// t1() -> 7 +// t2() -> 8 +// t3() -> 7 +// t4() -> 8 +// x() -> 2 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol b/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol new file mode 100644 index 000000000..9b20dbfd2 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol @@ -0,0 +1,16 @@ +contract C { + uint16 x = 0x1234; + uint16 a = 0xffff; + uint16 b; + + function f() public returns (uint256, uint256, uint256, uint256) { + a++; + uint256 c = b; + delete b; + a -= 2; + return (x, c, b, a); + } +} + +// ---- +// f() -> 0x1234, 0x0, 0x0, 0xfffe diff --git a/test/libsolidity/semanticTests/storage/packed_storage_signed.sol b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol new file mode 100644 index 000000000..8db0c757e --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol @@ -0,0 +1,22 @@ +contract C { + int8 a; + uint8 b; + int8 c; + uint8 d; + + function test() + public + returns (uint256 x1, uint256 x2, uint256 x3, uint256 x4) + { + a = -2; + b = -uint8(a) * 2; + c = a * int8(120) * int8(121); + x1 = uint256(a); + x2 = b; + x3 = uint256(c); + x4 = d; + } +} + +// ---- +// test() -> -2, 4, -112, 0 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol new file mode 100644 index 000000000..daeee6ba6 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol @@ -0,0 +1,45 @@ +contract C { + struct s1 { + bytes1 a; + bytes1 b; + bytes10 c; + bytes9 d; + bytes10 e; + } + struct s2 { + bytes1 a; + s1 inner; + bytes1 b; + bytes1 c; + } + bytes1 x; + s2 data; + bytes1 y; + + function test() public returns (bool) { + x = 0x01; + data.a = 0x02; + data.inner.a = 0x03; + data.inner.b = 0x04; + data.inner.c = "1234567890"; + data.inner.d = "123456789"; + data.inner.e = "abcdefghij"; + data.b = 0x05; + data.c = bytes1(0x06); + y = 0x07; + return + x == 0x01 && + data.a == 0x02 && + data.inner.a == 0x03 && + data.inner.b == 0x04 && + data.inner.c == "1234567890" && + data.inner.d == "123456789" && + data.inner.e == "abcdefghij" && + data.b == 0x05 && + data.c == bytes1(0x06) && + y == 0x07; + } +} + +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol new file mode 100644 index 000000000..9c9f778f0 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol @@ -0,0 +1,33 @@ +contract C { + enum small {A, B, C, D} + enum larger {A, B, C, D, E} + struct str { + small a; + small b; + larger c; + larger d; + } + str data; + + function test() public returns (uint256) { + data.a = small.B; + if (data.a != small.B) return 2; + data.b = small.C; + if (data.b != small.C) return 3; + data.c = larger.D; + if (data.c != larger.D) return 4; + if (data.a != small.B) return 5; + data.a = small.C; + if (data.a != small.C) return 6; + if (data.b != small.C) return 7; + data.b = small.D; + if (data.b != small.D) return 8; + if (data.c != larger.D) return 9; + data.c = larger.B; + if (data.c != larger.B) return 10; + return 1; + } +} + +// ---- +// test() -> 1 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol new file mode 100644 index 000000000..8c91576f5 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol @@ -0,0 +1,30 @@ +contract C { + struct str { + uint8 a; + uint16 b; + uint248 c; + } + str data; + + function test() public returns (uint256) { + data.a = 2; + if (data.a != 2) return 2; + data.b = 0xabcd; + if (data.b != 0xabcd) return 3; + data.c = 0x1234567890; + if (data.c != 0x1234567890) return 4; + if (data.a != 2) return 5; + data.a = 8; + if (data.a != 8) return 6; + if (data.b != 0xabcd) return 7; + data.b = 0xdcab; + if (data.b != 0xdcab) return 8; + if (data.c != 0x1234567890) return 9; + data.c = 0x9876543210; + if (data.c != 0x9876543210) return 10; + return 1; + } +} + +// ---- +// test() -> 1 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol new file mode 100644 index 000000000..a7ecf2bd0 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol @@ -0,0 +1,17 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S calldata s) external pure returns (uint256 a, uint256 b) { + a = s.a; + b = s.b; + } +} + +// ---- +// f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol new file mode 100644 index 000000000..c3b8249e8 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol @@ -0,0 +1,20 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(uint256 a, S calldata s, uint256 b) + external + pure + returns (uint256, uint256, uint256, uint256) + { + return (a, s.a, s.b, b); + } +} + +// ---- +// f(uint256,(uint256,uint256),uint256): 1, 2, 3, 4 -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol new file mode 100644 index 000000000..3e8613fad --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256[2] b; + uint256 c; + } + + function f(S calldata s) + external + pure + returns (uint256 a, uint256 b0, uint256 b1, uint256 c) + { + a = s.a; + b0 = s.b[0]; + b1 = s.b[1]; + c = s.c; + } +} + +// ---- +// f((uint256,uint256[2],uint256)): 42, 1, 2, 23 -> 42, 1, 2, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol new file mode 100644 index 000000000..db8171a20 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol @@ -0,0 +1,17 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint256 a; + uint256 b; + } + + function f(S calldata s) external pure returns (uint256, uint256) { + S memory m = s; + return (m.a, m.b); + } +} + +// ---- +// f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol new file mode 100644 index 000000000..4139c1073 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol @@ -0,0 +1,27 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S1 { + uint256 a; + uint256 b; + } + struct S2 { + uint256 a; + } + + function f(S1 calldata s1, S2 calldata s2, S1 calldata s3) + external + pure + returns (uint256 a, uint256 b, uint256 c, uint256 d, uint256 e) + { + a = s1.a; + b = s1.b; + c = s2.a; + d = s3.a; + e = s3.b; + } +} + +// ---- +// f((uint256,uint256),(uint256),(uint256,uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol b/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol new file mode 100644 index 000000000..829345d2e --- /dev/null +++ b/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol @@ -0,0 +1,16 @@ +contract C { + struct s { + uint256 a; + uint256 b; + } + + function f() public returns (uint256) { + s[7][]; // This is only the type, should not have any effect + return 3; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 3 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol new file mode 100644 index 000000000..1173a3796 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol @@ -0,0 +1,32 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + + function test() public returns (uint256 x, uint256 y, uint256 z) { + S memory data = combine(1, 2, 3); + x = extract(data, 0); + y = extract(data, 1); + z = extract(data, 2); + } + + function extract(S memory s, uint256 which) internal returns (uint256 x) { + if (which == 0) return s.x; + else if (which == 1) return s.y; + else return s.z; + } + + function combine(uint8 x, uint16 y, uint256 z) + internal + returns (S memory s) + { + s.x = x; + s.y = y; + s.z = z; + } +} + +// ---- +// test() -> 1, 2, 3 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol new file mode 100644 index 000000000..f5c41a232 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol @@ -0,0 +1,42 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + struct X { + uint8 x; + S s; + } + + function test() + public + returns (uint256 a, uint256 x, uint256 y, uint256 z) + { + X memory d = combine(1, 2, 3, 4); + a = extract(d, 0); + x = extract(d, 1); + y = extract(d, 2); + z = extract(d, 3); + } + + function extract(X memory s, uint256 which) internal returns (uint256 x) { + if (which == 0) return s.x; + else if (which == 1) return s.s.x; + else if (which == 2) return s.s.y; + else return s.s.z; + } + + function combine(uint8 a, uint8 x, uint16 y, uint256 z) + internal + returns (X memory s) + { + s.x = a; + s.s.x = x; + s.s.y = y; + s.s.z = z; + } +} + +// ---- +// test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol new file mode 100644 index 000000000..c16705643 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol @@ -0,0 +1,69 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + struct X { + uint8 x; + S s; + uint8[2] a; + } + X m_x; + + function load() + public + returns ( + uint256 a, + uint256 x, + uint256 y, + uint256 z, + uint256 a1, + uint256 a2 + ) + { + m_x.x = 1; + m_x.s.x = 2; + m_x.s.y = 3; + m_x.s.z = 4; + m_x.a[0] = 5; + m_x.a[1] = 6; + X memory d = m_x; + a = d.x; + x = d.s.x; + y = d.s.y; + z = d.s.z; + a1 = d.a[0]; + a2 = d.a[1]; + } + + function store() + public + returns ( + uint256 a, + uint256 x, + uint256 y, + uint256 z, + uint256 a1, + uint256 a2 + ) + { + X memory d; + d.x = 1; + d.s.x = 2; + d.s.y = 3; + d.s.z = 4; + d.a[0] = 5; + d.a[1] = 6; + m_x = d; + a = m_x.x; + x = m_x.s.x; + y = m_x.s.y; + z = m_x.s.z; + a1 = m_x.a[0]; + a2 = m_x.a[1]; + } +} +// ---- +// load() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 +// store() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_read_write.sol b/test/libsolidity/semanticTests/structs/memory_structs_read_write.sol new file mode 100644 index 000000000..105f59909 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_read_write.sol @@ -0,0 +1,56 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + uint8[2] a; + } + S[5] data; + + function testInit() + public + returns (uint8 x, uint16 y, uint256 z, uint8 a, bool flag) + { + S[2] memory d; + x = d[0].x; + y = d[0].y; + z = d[0].z; + a = d[0].a[1]; + flag = true; + } + + function testCopyRead() + public + returns (uint8 x, uint16 y, uint256 z, uint8 a) + { + data[2].x = 1; + data[2].y = 2; + data[2].z = 3; + data[2].a[1] = 4; + S memory s = data[2]; + x = s.x; + y = s.y; + z = s.z; + a = s.a[1]; + } + + function testAssign() + public + returns (uint8 x, uint16 y, uint256 z, uint8 a) + { + S memory s; + s.x = 1; + s.y = 2; + s.z = 3; + s.a[1] = 4; + x = s.x; + y = s.y; + z = s.z; + a = s.a[1]; + } +} + +// ---- +// testInit() -> 0, 0, 0, 0, true +// testCopyRead() -> 1, 2, 3, 4 +// testAssign() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_with_mappings.sol b/test/libsolidity/semanticTests/structs/memory_structs_with_mappings.sol new file mode 100644 index 000000000..3aaa354f8 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/memory_structs_with_mappings.sol @@ -0,0 +1,24 @@ +contract Test { + struct S { + uint8 a; + mapping(uint256 => uint256) b; + uint8 c; + } + S s; + + function f() public returns (uint256) { + S memory x; + if (x.a != 0 || x.c != 0) return 1; + x.a = 4; + x.c = 5; + s = x; + if (s.a != 4 || s.c != 5) return 2; + x = S(2, 3); + if (x.a != 2 || x.c != 3) return 3; + x = s; + if (s.a != 4 || s.c != 5) return 4; + } +} + +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/structs/recursive_structs.sol b/test/libsolidity/semanticTests/structs/recursive_structs.sol new file mode 100644 index 000000000..da568a255 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/recursive_structs.sol @@ -0,0 +1,19 @@ +contract C { + struct S { + S[] x; + } + S sstorage; + + function f() public returns (uint256) { + S memory s; + s.x = new S[](10); + delete s; + // TODO Uncomment after implemented. + // sstorage.x.push(); + delete sstorage; + return 1; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol new file mode 100644 index 000000000..8591131be --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol @@ -0,0 +1,36 @@ +contract test { + struct testStruct { + uint256 m_value; + } + testStruct data1; + testStruct data2; + testStruct data3; + + constructor() public { + data1.m_value = 2; + } + + function assign() + public + returns ( + uint256 ret_local, + uint256 ret_global, + uint256 ret_global3, + uint256 ret_global1 + ) + { + testStruct storage x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 + data2 = data1; // should copy data. data2.m_value == 2 + + ret_local = x.m_value; // = 2 + ret_global = data2.m_value; // = 2 + + x.m_value = 3; + data3 = x; //should copy the data. data3.m_value == 3 + ret_global3 = data3.m_value; // = 3 + ret_global1 = data1.m_value; // = 3. Changed due to the assignment to x.m_value + } +} + +// ---- +// assign() -> 2, 2, 3, 3 diff --git a/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol new file mode 100644 index 000000000..dbd67ab58 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol @@ -0,0 +1,30 @@ +contract C { + struct X { + uint256 x1; + uint256 x2; + } + struct S { + uint256 s1; + uint256[3] s2; + X s3; + } + S s; + + constructor() public { + uint256[3] memory s2; + s2[1] = 9; + s = S(1, s2, X(4, 5)); + } + + function get() + public + returns (uint256 s1, uint256[3] memory s2, uint256 x1, uint256 x2) + { + s1 = s.s1; + s2 = s.s2; + x1 = s.s3.x1; + x2 = s.s3.x2; + } +} +// ---- +// get() -> 0x01, 0x00, 0x09, 0x00, 0x04, 0x05 diff --git a/test/libsolidity/semanticTests/structs/struct_copy.sol b/test/libsolidity/semanticTests/structs/struct_copy.sol new file mode 100644 index 000000000..f6c35f7da --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_copy.sol @@ -0,0 +1,48 @@ +contract c { + struct Nested { + uint256 x; + uint256 y; + } + struct Struct { + uint256 a; + mapping(uint256 => Struct) b; + Nested nested; + uint256 c; + } + mapping(uint256 => Struct) data; + + function set(uint256 k) public returns (bool) { + data[k].a = 1; + data[k].nested.x = 3; + data[k].nested.y = 4; + data[k].c = 2; + return true; + } + + function copy(uint256 from, uint256 to) public returns (bool) { + data[to] = data[from]; + return true; + } + + function retrieve(uint256 k) + public + returns (uint256 a, uint256 x, uint256 y, uint256 c) + { + a = data[k].a; + x = data[k].nested.x; + y = data[k].nested.y; + c = data[k].c; + } +} + +// ---- +// set(uint256): 7 -> true +// retrieve(uint256): 7 -> 1, 3, 4, 2 +// copy(uint256,uint256): 7, 8 -> true +// retrieve(uint256): 7 -> 1, 3, 4, 2 +// retrieve(uint256): 8 -> 1, 3, 4, 2 +// copy(uint256,uint256): 0, 7 -> true +// retrieve(uint256): 7 -> 0, 0, 0, 0 +// retrieve(uint256): 8 -> 1, 3, 4, 2 +// copy(uint256,uint256): 7, 8 -> true +// retrieve(uint256): 8 -> 0, 0, 0, 0 diff --git a/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol new file mode 100644 index 000000000..a1cfcac05 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol @@ -0,0 +1,19 @@ +contract c { + struct Struct { + uint256 a; + uint256 b; + } + Struct data1; + Struct data2; + + function test() public returns (bool) { + data1.a = 1; + data1.b = 2; + Struct memory x = data1; + data2 = x; + return data2.a == data1.a && data2.b == data1.b; + } +} + +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/structs/struct_delete_member.sol b/test/libsolidity/semanticTests/structs/struct_delete_member.sol new file mode 100644 index 000000000..fbaf7b6d9 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_member.sol @@ -0,0 +1,20 @@ +contract test { + struct testStruct { + uint256 m_value; + } + testStruct data1; + + constructor() public { + data1.m_value = 2; + } + + function deleteMember() public returns (uint256 ret_value) { + testStruct storage x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 + x.m_value = 4; + delete x.m_value; + ret_value = data1.m_value; + } +} + +// ---- +// deleteMember() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol b/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol new file mode 100644 index 000000000..59d79da9f --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol @@ -0,0 +1,18 @@ +contract test { + struct testStruct { + uint256 m_value; + } + mapping(uint256 => testStruct) campaigns; + + constructor() public { + campaigns[0].m_value = 2; + } + + function deleteIt() public returns (uint256) { + delete campaigns[0]; + return campaigns[0].m_value; + } +} + +// ---- +// deleteIt() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_named_constructor.sol b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol new file mode 100644 index 000000000..5368b090e --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol @@ -0,0 +1,14 @@ +contract C { + struct S { + uint256 a; + bool x; + } + S public s; + + constructor() public { + s = S({a: 1, x: true}); + } +} + +// ---- +// s() -> 1, true diff --git a/test/libsolidity/semanticTests/types/tuple_in_tuple.sol b/test/libsolidity/semanticTests/types/tuple_in_tuple.sol new file mode 100644 index 000000000..d9737a807 --- /dev/null +++ b/test/libsolidity/semanticTests/types/tuple_in_tuple.sol @@ -0,0 +1,24 @@ +contract test { + function f0() public returns(int, bool) { + int a; + bool b; + ((a, b)) = (2, true); + return (a, b); + } + function f1() public returns(int) { + int a; + (((a, ), )) = ((1, 2) ,3); + return a; + } + function f2() public returns(int) { + int a; + (((, a),)) = ((1, 2), 3); + return a; + } +} +// ==== +// compileViaYul: also +// ---- +// f0() -> 2, true +// f1() -> 1 +// f2() -> 2 diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol b/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol new file mode 100644 index 000000000..f6fa4d45e --- /dev/null +++ b/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + +struct S { uint256 v; string s; } + +contract A +{ + function test() external virtual returns (uint256 v, string memory s) + { + v = 42; + s = "test"; + } +} +contract X is A +{ + S public override test; + + function set() public { test.v = 2; test.s = "statevar"; } +} + + +// ---- +// test() -> 0, 64, 0 +// set() -> +// test() -> 2, 0x40, 8, "statevar" diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol b/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol new file mode 100644 index 000000000..404013ee6 --- /dev/null +++ b/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; + +struct S { uint256 v; string s; } + +contract A +{ + function test(uint256 x) external virtual returns (uint256 v, string memory s) + { + v = x; + s = "test"; + } +} +contract X is A +{ + mapping(uint256 => S) public override test; + + function set() public { test[42].v = 2; test[42].s = "statevar"; } +} + + +// ---- +// test(uint256): 0 -> 0, 64, 0 +// test(uint256): 42 -> 0, 64, 0 +// set() -> +// test(uint256): 0 -> 0, 64, 0 +// test(uint256): 42 -> 2, 0x40, 8, "statevar" diff --git a/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol new file mode 100644 index 000000000..b83b1c598 --- /dev/null +++ b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol @@ -0,0 +1,10 @@ +contract C { + uint256 constant x = 0x123 + 0x456; + + function f() public returns (uint256) { + return x + 1; + } +} + +// ---- +// f() -> 0x57a diff --git a/test/libsolidity/semanticTests/various/balance.sol b/test/libsolidity/semanticTests/various/balance.sol new file mode 100644 index 000000000..d6a80dbf9 --- /dev/null +++ b/test/libsolidity/semanticTests/various/balance.sol @@ -0,0 +1,13 @@ +contract test { + constructor() public payable {} + + function getBalance() public returns (uint256 balance) { + return address(this).balance; + } +} + +// ==== +// compileViaYul: also +// ---- +// constructor(), 23 wei -> +// getBalance() -> 23 diff --git a/test/libsolidity/semanticTests/various/byte_optimization_bug.sol b/test/libsolidity/semanticTests/various/byte_optimization_bug.sol new file mode 100644 index 000000000..3d94e4333 --- /dev/null +++ b/test/libsolidity/semanticTests/various/byte_optimization_bug.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint256 x) public returns (uint256 a) { + assembly { + a := byte(x, 31) + } + } + + function g(uint256 x) public returns (uint256 a) { + assembly { + a := byte(31, x) + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 2 -> 0 +// g(uint256): 2 -> 2 diff --git a/test/libsolidity/semanticTests/various/code_access_content.sol b/test/libsolidity/semanticTests/various/code_access_content.sol new file mode 100644 index 000000000..2c115d6c8 --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_content.sol @@ -0,0 +1,42 @@ +contract D { + bytes32 public x; + + constructor() public { + bytes32 codeHash; + assembly { + let size := codesize() + codecopy(mload(0x40), 0, size) + codeHash := keccak256(mload(0x40), size) + } + x = codeHash; + } +} + + +contract C { + function testRuntime() public returns (bool) { + D d = new D(); + bytes32 runtimeHash = keccak256(type(D).runtimeCode); + bytes32 otherHash; + uint256 size; + assembly { + size := extcodesize(d) + extcodecopy(d, mload(0x40), 0, size) + otherHash := keccak256(mload(0x40), size) + } + require(size == type(D).runtimeCode.length); + require(runtimeHash == otherHash); + return true; + } + + function testCreation() public returns (bool) { + D d = new D(); + bytes32 creationHash = keccak256(type(D).creationCode); + require(creationHash == d.x()); + return true; + } +} + +// ---- +// testRuntime() -> true +// testCreation() -> true diff --git a/test/libsolidity/semanticTests/various/code_access_create.sol b/test/libsolidity/semanticTests/various/code_access_create.sol new file mode 100644 index 000000000..3fbcf6132 --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_create.sol @@ -0,0 +1,26 @@ +contract D { + uint256 x; + + constructor() public { + x = 7; + } + + function f() public view returns (uint256) { + return x; + } +} + + +contract C { + function test() public returns (uint256) { + bytes memory c = type(D).creationCode; + D d; + assembly { + d := create(0, add(c, 0x20), mload(c)) + } + return d.f(); + } +} + +// ---- +// test() -> 7 diff --git a/test/libsolidity/semanticTests/various/code_access_padding.sol b/test/libsolidity/semanticTests/various/code_access_padding.sol new file mode 100644 index 000000000..ecad28a16 --- /dev/null +++ b/test/libsolidity/semanticTests/various/code_access_padding.sol @@ -0,0 +1,19 @@ +contract D { + function f() public pure returns (uint256) { + return 7; + } +} + + +contract C { + function diff() public pure returns (uint256 remainder) { + bytes memory a = type(D).creationCode; + bytes memory b = type(D).runtimeCode; + assembly { + remainder := mod(sub(b, a), 0x20) + } + } +} + +// ---- +// diff() -> 0 # This checks that the allocation function pads to multiples of 32 bytes # diff --git a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol new file mode 100644 index 000000000..f1220d351 --- /dev/null +++ b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol @@ -0,0 +1,20 @@ +contract A { + function f() public { + new B(); + } +} + + +contract B { + function f() public {} +} + + +contract C { + function f() public { + new B(); + } +} + +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol b/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol new file mode 100644 index 000000000..fdd7aa32e --- /dev/null +++ b/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol @@ -0,0 +1,15 @@ +contract C { + function f() public returns (uint256 r) { + uint256; + uint256; + uint256; + uint256; + int256 x = -7; + return uint256(x); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> -7 diff --git a/test/libsolidity/semanticTests/various/cross_contract_types.sol b/test/libsolidity/semanticTests/various/cross_contract_types.sol new file mode 100644 index 000000000..47842cc03 --- /dev/null +++ b/test/libsolidity/semanticTests/various/cross_contract_types.sol @@ -0,0 +1,17 @@ +contract Lib { + struct S { + uint256 a; + uint256 b; + } +} + + +contract Test { + function f() public returns (uint256 r) { + Lib.S memory x = Lib.S({a: 2, b: 3}); + r = x.b; + } +} + +// ---- +// f() -> 3 diff --git a/test/libsolidity/semanticTests/various/decayed_tuple.sol b/test/libsolidity/semanticTests/various/decayed_tuple.sol new file mode 100644 index 000000000..b00942cb3 --- /dev/null +++ b/test/libsolidity/semanticTests/various/decayed_tuple.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (uint256) { + uint256 x = 1; + (x) = 2; + return x; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol new file mode 100644 index 000000000..0c8dc3835 --- /dev/null +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -0,0 +1,36 @@ +contract C { + uint256 x = 7; + bytes data; + uint256[] y; + uint256[] arrayData; + + function returnsArray() public returns (uint256[] memory) { + arrayData = new uint256[](9); + arrayData[2] = 5; + arrayData[7] = 4; + return arrayData; + } + + function f(bytes memory s) public returns (uint256) { + uint256 loc; + uint256[] memory memArray; + (loc, x, y, data, arrayData[3]) = (8, 4, returnsArray(), s, 2); + if (loc != 8) return 1; + if (x != 4) return 2; + if (y.length != 9) return 3; + if (y[2] != 5) return 4; + if (y[7] != 4) return 5; + if (data.length != s.length) return 6; + if (data[3] != s[3]) return 7; + if (arrayData[3] != 2) return 8; + (memArray, loc) = (arrayData, 3); + if (loc != 3) return 9; + if (memArray.length != arrayData.length) return 10; + bytes memory memBytes; + (x, memBytes, y[2], , ) = (456, s, 789, 101112, 131415); + if (x != 456 || memBytes.length != s.length || y[2] != 789) return 11; + } +} + +// ---- +// f(bytes): 0x20, 0x5, "abcde" -> 0 diff --git a/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol b/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol new file mode 100644 index 000000000..d1ea9ab34 --- /dev/null +++ b/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol @@ -0,0 +1,10 @@ +contract test { + function f(uint256 k) public returns (uint256) { + return k; + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 9 -> 9 diff --git a/test/libsolidity/semanticTests/various/external_types_in_calls.sol b/test/libsolidity/semanticTests/various/external_types_in_calls.sol new file mode 100644 index 000000000..9906bd52b --- /dev/null +++ b/test/libsolidity/semanticTests/various/external_types_in_calls.sol @@ -0,0 +1,28 @@ +contract C1 { + C1 public bla; + + constructor(C1 x) public { + bla = x; + } +} + + +contract C { + function test() public returns (C1 x, C1 y) { + C1 c = new C1(C1(9)); + x = c.bla(); + y = this.t1(C1(7)); + } + + function t1(C1 a) public returns (C1) { + return a; + } + + function t2() public returns (C1) { + return C1(9); + } +} + +// ---- +// test() -> 9, 7 +// t2() -> 9 diff --git a/test/libsolidity/semanticTests/various/flipping_sign_tests.sol b/test/libsolidity/semanticTests/various/flipping_sign_tests.sol new file mode 100644 index 000000000..8309615a5 --- /dev/null +++ b/test/libsolidity/semanticTests/various/flipping_sign_tests.sol @@ -0,0 +1,10 @@ +contract test { + function f() public returns (bool) { + int256 x = -2**255; + assert(-x == x); + return true; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/gasleft_decrease.sol b/test/libsolidity/semanticTests/various/gasleft_decrease.sol new file mode 100644 index 000000000..8de56296e --- /dev/null +++ b/test/libsolidity/semanticTests/various/gasleft_decrease.sol @@ -0,0 +1,20 @@ +contract C { + uint256 v; + + function f() public returns (bool) { + uint256 startGas = gasleft(); + v++; + assert(startGas > gasleft()); + return true; + } + + function g() public returns (bool) { + uint256 startGas = gasleft(); + assert(startGas > gasleft()); + return true; + } +} + +// ---- +// f() -> true +// g() -> true diff --git a/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol b/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol new file mode 100644 index 000000000..00c0eabed --- /dev/null +++ b/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol @@ -0,0 +1,14 @@ +contract C { + function gasleft() public returns (uint256) { + return 0; + } + + function f() public returns (uint256) { + return gasleft(); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/various/inline_member_init.sol b/test/libsolidity/semanticTests/various/inline_member_init.sol new file mode 100644 index 000000000..5dca66d80 --- /dev/null +++ b/test/libsolidity/semanticTests/various/inline_member_init.sol @@ -0,0 +1,19 @@ +contract test { + constructor() public { + m_b = 6; + m_c = 8; + } + + uint256 m_a = 5; + uint256 m_b; + uint256 m_c = 7; + + function get() public returns (uint256 a, uint256 b, uint256 c) { + a = m_a; + b = m_b; + c = m_c; + } +} + +// ---- +// get() -> 5, 6, 8 diff --git a/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol new file mode 100644 index 000000000..53f5b3718 --- /dev/null +++ b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol @@ -0,0 +1,24 @@ +contract Base { + constructor() public {} + + uint256 m_base = 5; + + function getBMember() public returns (uint256 i) { + return m_base; + } +} + + +contract Derived is Base { + constructor() public {} + + uint256 m_derived = 6; + + function getDMember() public returns (uint256 i) { + return m_derived; + } +} + +// ---- +// getBMember() -> 5 +// getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol b/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol new file mode 100644 index 000000000..e350a11d3 --- /dev/null +++ b/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol @@ -0,0 +1,9 @@ +contract c { + function f() public returns (int8) { + int8[5] memory foo3 = [int8(1), -1, 0, 0, 0]; + return foo3[0]; + } +} + +// ---- +// f() -> 1 diff --git a/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol b/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol new file mode 100644 index 000000000..743825d60 --- /dev/null +++ b/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol @@ -0,0 +1,19 @@ +// A long time ago, some opcodes were renamed, which involved the opcodes +// "iszero" and "not". +contract C { + function f() public returns (bool) { + bytes32 x = bytes32(uint256(1)); + assembly { + x := not(x) + } + if (x != ~bytes32(uint256(1))) return false; + assembly { + x := iszero(x) + } + if (x != bytes32(0)) return false; + return true; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/literal_empty_string.sol b/test/libsolidity/semanticTests/various/literal_empty_string.sol new file mode 100644 index 000000000..bf4da5409 --- /dev/null +++ b/test/libsolidity/semanticTests/various/literal_empty_string.sol @@ -0,0 +1,20 @@ +contract C { + bytes32 public x; + uint256 public a; + + function f(bytes32 _x, uint256 _a) public { + x = _x; + a = _a; + } + + function g() public { + this.f("", 2); + } +} + +// ---- +// x() -> 0 +// a() -> 0 +// g() -> +// x() -> 0 +// a() -> 2 diff --git a/test/libsolidity/semanticTests/various/memory_overwrite.sol b/test/libsolidity/semanticTests/various/memory_overwrite.sol new file mode 100644 index 000000000..7312509e2 --- /dev/null +++ b/test/libsolidity/semanticTests/various/memory_overwrite.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (bytes memory x) { + x = "12345"; + x[3] = 0x61; + x[0] = 0x62; + } +} + +// ---- +// f() -> 0x20, 5, "b23a5" diff --git a/test/libsolidity/semanticTests/various/multi_modifiers.sol b/test/libsolidity/semanticTests/various/multi_modifiers.sol new file mode 100644 index 000000000..07f6c038d --- /dev/null +++ b/test/libsolidity/semanticTests/various/multi_modifiers.sol @@ -0,0 +1,23 @@ +// This triggered a bug in some version because the variable in the modifier was not +// unregistered correctly. +contract C { + uint256 public x; + modifier m1 { + address a1 = msg.sender; + x++; + _; + } + + function f1() public m1() { + x += 7; + } + + function f2() public m1() { + x += 3; + } +} +// ---- +// f1() -> +// x() -> 0x08 +// f2() -> +// x() -> 0x0c diff --git a/test/libsolidity/semanticTests/various/multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol new file mode 100644 index 000000000..6f79442e7 --- /dev/null +++ b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol @@ -0,0 +1,47 @@ +contract C { + function g() public returns (uint256 a, uint256 b, uint256 c) { + a = 1; + b = 2; + c = 3; + } + + function h() public returns (uint256 a, uint256 b, uint256 c, uint256 d) { + a = 1; + b = 2; + c = 3; + d = 4; + } + + function f1() public returns (bool) { + (uint256 x, uint256 y, uint256 z) = g(); + if (x != 1 || y != 2 || z != 3) return false; + (, uint256 a, ) = g(); + if (a != 2) return false; + (uint256 b, , ) = g(); + if (b != 1) return false; + (, , uint256 c) = g(); + if (c != 3) return false; + return true; + } + + function f2() public returns (bool) { + (uint256 a1, , uint256 a3, ) = h(); + if (a1 != 1 || a3 != 3) return false; + (uint256 b1, uint256 b2, , ) = h(); + if (b1 != 1 || b2 != 2) return false; + (, uint256 c2, uint256 c3, ) = h(); + if (c2 != 2 || c3 != 3) return false; + (, , uint256 d3, uint256 d4) = h(); + if (d3 != 3 || d4 != 4) return false; + (uint256 e1, , uint256 e3, uint256 e4) = h(); + if (e1 != 1 || e3 != 3 || e4 != 4) return false; + return true; + } + + function f() public returns (bool) { + return f1() && f2(); + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/negative_stack_height.sol b/test/libsolidity/semanticTests/various/negative_stack_height.sol new file mode 100644 index 000000000..d5074dc91 --- /dev/null +++ b/test/libsolidity/semanticTests/various/negative_stack_height.sol @@ -0,0 +1,63 @@ +contract C { + mapping(uint256 => Invoice) public invoices; + struct Invoice { + uint256 AID; + bool Aboola; + bool Aboolc; + bool exists; + } + + function nredit(uint256 startindex) + public + pure + returns ( + uint256[500] memory CIDs, + uint256[500] memory dates, + uint256[500] memory RIDs, + bool[500] memory Cboolas, + uint256[500] memory amounts + ) + {} + + function return500InvoicesByDates( + uint256 begindate, + uint256 enddate, + uint256 startindex + ) + public + view + returns ( + uint256[500] memory AIDs, + bool[500] memory Aboolas, + uint256[500] memory dates, + bytes32[3][500] memory Abytesas, + bytes32[3][500] memory bytesbs, + bytes32[2][500] memory bytescs, + uint256[500] memory amounts, + bool[500] memory Aboolbs, + bool[500] memory Aboolcs + ) + {} + + function return500PaymentsByDates( + uint256 begindate, + uint256 enddate, + uint256 startindex + ) + public + view + returns ( + uint256[500] memory BIDs, + uint256[500] memory dates, + uint256[500] memory RIDs, + bool[500] memory Bboolas, + bytes32[3][500] memory bytesbs, + bytes32[2][500] memory bytescs, + uint256[500] memory amounts, + bool[500] memory Bboolbs + ) + {} +} + +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct.sol new file mode 100644 index 000000000..f02fda353 --- /dev/null +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct.sol @@ -0,0 +1,26 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S1 { + uint256 a; + uint256 b; + } + struct S2 { + uint256 a; + uint256 b; + S1 s; + uint256 c; + } + + function f(S2 calldata s) + external + pure + returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) + { + return (s.a, s.b, s.s.a, s.s.b, s.c); + } +} + +// ---- +// f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol new file mode 100644 index 000000000..e0a25a857 --- /dev/null +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol @@ -0,0 +1,27 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S1 { + uint256 a; + uint256 b; + } + struct S2 { + uint256 a; + uint256 b; + S1 s; + uint256 c; + } + + function f(S2 calldata s) + external + pure + returns (uint256 a, uint256 b, uint256 sa, uint256 sb, uint256 c) + { + S2 memory m = s; + return (m.a, m.b, m.s.a, m.s.b, m.c); + } +} + +// ---- +// f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol new file mode 100644 index 000000000..9ba67b198 --- /dev/null +++ b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol @@ -0,0 +1,10 @@ +contract test { + int8 public x = 2; + int8 public y = 127; + int16 public q = 250; +} + +// ---- +// x() -> 2 +// y() -> 127 +// q() -> 250 diff --git a/test/libsolidity/semanticTests/various/senders_balance.sol b/test/libsolidity/semanticTests/various/senders_balance.sol new file mode 100644 index 000000000..0c84352b6 --- /dev/null +++ b/test/libsolidity/semanticTests/various/senders_balance.sol @@ -0,0 +1,20 @@ +contract C { + function f() public view returns (uint256) { + return msg.sender.balance; + } +} + + +contract D { + C c = new C(); + + constructor() public payable {} + + function f() public view returns (uint256) { + return c.f(); + } +} + +// ---- +// constructor(), 27 wei -> +// f() -> 27 diff --git a/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol b/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol new file mode 100644 index 000000000..0864121de --- /dev/null +++ b/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol @@ -0,0 +1,35 @@ +contract Base { + uint256 data; + + function setData(uint256 i) public { + data = i; + } + + function getViaBase() public returns (uint256 i) { + return data; + } +} + + +contract A is Base { + function setViaA(uint256 i) public { + setData(i); + } +} + + +contract B is Base { + function getViaB() public returns (uint256 i) { + return getViaBase(); + } +} + + +contract Derived is Base, B, A {} + +// ==== +// compileViaYul: also +// ---- +// getViaB() -> 0 +// setViaA(uint256): 23 -> +// getViaB() -> 23 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types.sol new file mode 100644 index 000000000..350ea2622 --- /dev/null +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types.sol @@ -0,0 +1,15 @@ +// The EVM cannot provide access to dynamically-sized return values, so we have to skip them. +contract C { + function f() public returns (uint256, uint256[] memory, uint256) { + return (7, new uint256[](2), 8); + } + + function g() public returns (uint256, uint256) { + // Previous implementation "moved" b to the second place and did not skip. + (uint256 a, , uint256 b) = this.f(); + return (a, b); + } +} + +// ---- +// g() -> 7, 8 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol new file mode 100644 index 000000000..3b80cf973 --- /dev/null +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -0,0 +1,22 @@ +// For accessors, the dynamic types are already removed in the external signature itself. +contract C { + struct S { + uint256 x; + string a; // this is present in the accessor + uint256[] b; // this is not present + uint256 y; + } + S public s; + + function g() public returns (uint256, uint256) { + s.x = 2; + s.a = "abc"; + s.b = [7, 8, 9]; + s.y = 6; + (uint256 x, , uint256 y) = this.s(); + return (x, y); + } +} + +// ---- +// g() -> 2, 6 diff --git a/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol b/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol new file mode 100644 index 000000000..585914c80 --- /dev/null +++ b/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol @@ -0,0 +1,11 @@ +contract A { + uint256 x = 1; + uint256 y = 2; + + function a() public returns (uint256 x) { + x = A.y; + } +} + +// ---- +// a() -> 2 diff --git a/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol b/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol new file mode 100644 index 000000000..6bef6f85a --- /dev/null +++ b/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol @@ -0,0 +1,10 @@ +contract Scope { + uint256 stateVar = 42; + + function getStateVar() public view returns (uint256 stateVar) { + stateVar = Scope.stateVar; + } +} + +// ---- +// getStateVar() -> 42 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol new file mode 100644 index 000000000..96c5419be --- /dev/null +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol @@ -0,0 +1,39 @@ +contract C { + uint256 x; + + function f() public returns (uint256) { + x = 3; + return 1; + } +} + + +interface CView { + function f() external view returns (uint256); +} + + +interface CPure { + function f() external pure returns (uint256); +} + + +contract D { + function f() public returns (uint256) { + return (new C()).f(); + } + + function fview() public returns (uint256) { + return (CView(address(new C()))).f(); + } + + function fpure() public returns (uint256) { + return (CPure(address(new C()))).f(); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// f() -> 0x1 # This should work, next should throw # +// fview() -> FAILURE +// fpure() -> FAILURE diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol new file mode 100644 index 000000000..a193e8752 --- /dev/null +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol @@ -0,0 +1,39 @@ +contract C { + uint256 x; + + function f() public returns (uint256) { + x = 3; + return 1; + } +} + + +interface CView { + function f() external view returns (uint256); +} + + +interface CPure { + function f() external pure returns (uint256); +} + + +contract D { + function f() public returns (uint256) { + return (new C()).f(); + } + + function fview() public returns (uint256) { + return (CView(address(new C()))).f(); + } + + function fpure() public returns (uint256) { + return (CPure(address(new C()))).f(); + } +} +// ==== +// EVMVersion: 0x1 +// fview() -> 1 +// fpure() -> 1 diff --git a/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol b/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol new file mode 100644 index 000000000..7a8a2c276 --- /dev/null +++ b/test/libsolidity/semanticTests/various/storage_string_as_mapping_key_without_variable.sol @@ -0,0 +1,11 @@ +contract Test { + mapping(string => uint256) data; + + function f() public returns (uint256) { + data["abc"] = 2; + return data["abc"]; + } +} + +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/various/store_bytes.sol b/test/libsolidity/semanticTests/various/store_bytes.sol new file mode 100644 index 000000000..99eb1acff --- /dev/null +++ b/test/libsolidity/semanticTests/various/store_bytes.sol @@ -0,0 +1,14 @@ +// this test just checks that the copy loop does not mess up the stack +contract C { + function save() public returns (uint256 r) { + r = 23; + savedData = msg.data; + r = 24; + } + + bytes savedData; +} + +// ---- +// save() -> 24 # empty copy loop # +// save(): "abcdefg" -> 24 diff --git a/test/libsolidity/semanticTests/various/string_tuples.sol b/test/libsolidity/semanticTests/various/string_tuples.sol new file mode 100644 index 000000000..3269d97c0 --- /dev/null +++ b/test/libsolidity/semanticTests/various/string_tuples.sol @@ -0,0 +1,17 @@ +contract C { + function f() public returns (string memory, uint256) { + return ("abc", 8); + } + + function g() public returns (string memory, string memory) { + return (h(), "def"); + } + + function h() public returns (string memory) { + return ("abc"); + } +} + +// ---- +// f() -> 0x40, 0x8, 0x3, "abc" +// g() -> 0x40, 0x80, 0x3, "abc", 0x3, "def" diff --git a/test/libsolidity/semanticTests/various/super.sol b/test/libsolidity/semanticTests/various/super.sol new file mode 100644 index 000000000..16e4257e1 --- /dev/null +++ b/test/libsolidity/semanticTests/various/super.sol @@ -0,0 +1,29 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + return super.f() | 4; + } +} + + +contract D is B, C { + function f() public override(B, C) returns (uint256 r) { + return super.f() | 8; + } +} + +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/various/super_alone.sol b/test/libsolidity/semanticTests/various/super_alone.sol new file mode 100644 index 000000000..623f25330 --- /dev/null +++ b/test/libsolidity/semanticTests/various/super_alone.sol @@ -0,0 +1,10 @@ +contract A { + function f() public { + super; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol new file mode 100644 index 000000000..59907b476 --- /dev/null +++ b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol @@ -0,0 +1,34 @@ +// This tests a swap in storage which does not work as one +// might expect because we do not have temporary storage. +// (x, y) = (y, x) is the same as +// y = x; +// x = y; +contract c { + struct S { + uint256 a; + uint256 b; + } + S public x; + S public y; + + function set() public { + x.a = 1; + x.b = 2; + y.a = 3; + y.b = 4; + } + + function swap() public { + (x, y) = (y, x); + } +} + +// ---- +// x() -> 0, 0 +// y() -> 0, 0 +// set() -> +// x() -> 1, 2 +// y() -> 3, 4 +// swap() -> +// x() -> 1, 2 +// y() -> 1, 2 diff --git a/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol new file mode 100644 index 000000000..1f12ba390 --- /dev/null +++ b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol @@ -0,0 +1,11 @@ +contract test { + function f(bool cond) public pure returns (uint256) { + uint32 x = 0x1234_ab; + uint256 y = 0x1234_abcd_1234; + return cond ? x : y; + } +} +// ---- +// f(bool): true -> 0x1234ab +// f(bool): false -> 0x1234abcd1234 + diff --git a/test/libsolidity/semanticTests/various/tuples.sol b/test/libsolidity/semanticTests/various/tuples.sol new file mode 100644 index 000000000..00fcd6f9a --- /dev/null +++ b/test/libsolidity/semanticTests/various/tuples.sol @@ -0,0 +1,30 @@ +contract C { + uint256[] data; + uint256[] m_c; + + function g() internal returns (uint256 a, uint256 b, uint256[] storage c) { + return (1, 2, data); + } + + function h() external returns (uint256 a, uint256 b) { + return (5, 6); + } + + function f() public returns (uint256) { + data.push(3); + uint256 a; + uint256 b; + (a, b) = this.h(); + if (a != 5 || b != 6) return 1; + uint256[] storage c = m_c; + (a, b, c) = g(); + if (a != 1 || b != 2 || c[0] != 3) return 2; + (a, b) = (b, a); + if (a != 2 || b != 1) return 3; + (a, , b, , ) = (8, 9, 10, 11, 12); + if (a != 8 || b != 10) return 4; + } +} + +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol new file mode 100644 index 000000000..1ab8586fc --- /dev/null +++ b/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol @@ -0,0 +1,26 @@ +contract C { + struct S { + uint256 x; + } + S s; + + function g() internal returns (uint256, S storage, uint256) { + s.x = 7; + return (1, s, 2); + } + + function f() public returns (bool) { + (uint256 x1, S storage y1, uint256 z1) = g(); + if (x1 != 1 || y1.x != 7 || z1 != 2) return false; + (, S storage y2, ) = g(); + if (y2.x != 7) return false; + (uint256 x2, , ) = g(); + if (x2 != 1) return false; + (, , uint256 z2) = g(); + if (z2 != 2) return false; + return true; + } +} + +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/various/value_complex.sol b/test/libsolidity/semanticTests/various/value_complex.sol new file mode 100644 index 000000000..19d11342f --- /dev/null +++ b/test/libsolidity/semanticTests/various/value_complex.sol @@ -0,0 +1,23 @@ +contract helper { + function getBalance() public payable returns (uint256 myBalance) { + return address(this).balance; + } +} + + +contract test { + helper h; + + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint256 amount) public payable returns (uint256 bal) { + uint256 someStackElement = 20; + return h.getBalance.value(amount).gas(1000).value(amount + 3)(); + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/value_insane.sol b/test/libsolidity/semanticTests/various/value_insane.sol new file mode 100644 index 000000000..d74a0f7f4 --- /dev/null +++ b/test/libsolidity/semanticTests/various/value_insane.sol @@ -0,0 +1,22 @@ +contract helper { + function getBalance() public payable returns (uint256 myBalance) { + return address(this).balance; + } +} + + +contract test { + helper h; + + constructor() public payable { + h = new helper(); + } + + function sendAmount(uint256 amount) public returns (uint256 bal) { + return h.getBalance.value(amount).gas(1000).value(amount + 3)(); // overwrite value + } +} + +// ---- +// constructor(), 20 wei -> +// sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/write_storage_external.sol b/test/libsolidity/semanticTests/various/write_storage_external.sol new file mode 100644 index 000000000..0bbe52248 --- /dev/null +++ b/test/libsolidity/semanticTests/various/write_storage_external.sol @@ -0,0 +1,40 @@ +contract C { + uint256 public x; + + function f(uint256 y) public payable { + x = y; + } + + function g(uint256 y) external { + x = y; + } + + function h() public { + this.g(12); + } +} + + +contract D { + C c = new C(); + + function f() public payable returns (uint256) { + c.g(3); + return c.x(); + } + + function g() public returns (uint256) { + c.g(8); + return c.x(); + } + + function h() public returns (uint256) { + c.h(); + return c.x(); + } +} + +// ---- +// f() -> 3 +// g() -> 8 +// h() -> 12 diff --git a/test/libsolidity/semanticTests/viaYul/array_2d_assignment.sol b/test/libsolidity/semanticTests/viaYul/array_2d_assignment.sol new file mode 100644 index 000000000..ff9c03cbe --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_2d_assignment.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][] memory a = new uint[][](2); + for (uint i = 0; i < 2; ++i) + a[i] = new uint[](3); + a[1][1] = n; + uint[] memory b = a[1]; + return b[1]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_2d_new.sol b/test/libsolidity/semanticTests/viaYul/array_2d_new.sol new file mode 100644 index 000000000..bfc8b2013 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_2d_new.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][] memory a = new uint[][](2); + for (uint i = 0; i < 2; ++i) + a[i] = new uint[](3); + return a[0][0] = n; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_3d_assignment.sol b/test/libsolidity/semanticTests/viaYul/array_3d_assignment.sol new file mode 100644 index 000000000..a2b3de54f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_3d_assignment.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][][] memory a = new uint[][][](2); + for (uint i = 0; i < 2; ++i) + { + a[i] = new uint[][](3); + for (uint j = 0; j < 3; ++j) + a[i][j] = new uint[](4); + } + a[1][1][1] = n; + uint[][] memory b = a[1]; + uint[] memory c = b[1]; + return c[1]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_3d_new.sol b/test/libsolidity/semanticTests/viaYul/array_3d_new.sol new file mode 100644 index 000000000..c3ff22ca3 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_3d_new.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint n) public pure returns (uint) { + uint[][][] memory a = new uint[][][](2); + for (uint i = 0; i < 2; ++i) + { + a[i] = new uint[][](3); + for (uint j = 0; j < 3; ++j) + a[i][j] = new uint[](4); + } + return a[1][1][1] = n; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 42 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol b/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol new file mode 100644 index 000000000..5344ae67f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol @@ -0,0 +1,29 @@ +contract C { + function f(uint n, uint m) public { + function() internal returns (uint)[] memory arr = new function() internal returns (uint)[](n); + arr[m](); + } + function f2(uint n, uint m, uint a, uint b) public { + function() internal returns (uint)[][] memory arr = new function() internal returns (uint)[][](n); + for (uint i = 0; i < n; ++i) + arr[i] = new function() internal returns (uint)[](m); + arr[a][b](); + } + function g(uint n, uint m) public { + function() external returns (uint)[] memory arr = new function() external returns (uint)[](n); + arr[m](); + } + function g2(uint n, uint m, uint a, uint b) public { + function() external returns (uint)[][] memory arr = new function() external returns (uint)[][](n); + for (uint i = 0; i < n; ++i) + arr[i] = new function() external returns (uint)[](m); + arr[a][b](); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256): 1823621, 12323 -> FAILURE +// f2(uint256,uint256,uint256,uint256): 18723921, 1823621, 123, 12323 -> FAILURE +// g(uint256,uint256): 1823621, 12323 -> FAILURE +// g2(uint256,uint256,uint256,uint256): 18723921, 1823621, 123, 12323 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol new file mode 100644 index 000000000..aa1349cb8 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol @@ -0,0 +1,24 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint m, uint a, uint b) public returns (uint) { + map[s] = 0; + uint[][] memory x = new uint[][](n); + for (uint i = 0; i < n; ++i) + x[i] = new uint[](m); + return x[a][b]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 0, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 3, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 3, 3, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 5, 32, "01234567890123456789012345678901" -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol new file mode 100644 index 000000000..1f490ba59 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol @@ -0,0 +1,22 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint m) public returns (uint) { + map[s] = 0; + uint[4][] memory x = new uint[4][](n); + return x[m][0]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256): 0x60, 2, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 2, 1, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 2, 2, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256): 0x60, 200, 199, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol new file mode 100644 index 000000000..31dd0a66a --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol @@ -0,0 +1,17 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s) public returns (uint[3] memory x, uint[2] memory y, uint[] memory z, uint t) { + map[s] = 0; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> 0, 0, 0, 0, 0, 0xe0, 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol new file mode 100644 index 000000000..6cf73bd8e --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol @@ -0,0 +1,19 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s) public returns (uint) { + map[s] = 0; + uint[3] memory x; + return x[2]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol new file mode 100644 index 000000000..1f9e70db7 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol @@ -0,0 +1,22 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint a) public returns (uint) { + map[s] = 0; + uint[] memory x = new uint[](n); + return x[a]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256): 0x60, 5, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 1, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 4, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 5, 32, "01234567890123456789012345678901" -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol new file mode 100644 index 000000000..d0e4ee595 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol @@ -0,0 +1,45 @@ +pragma experimental ABIEncoderV2; +contract C { + function f(uint256[] calldata x, uint256 s, uint256 e) external returns (uint256) { + return uint256[](x[s:e]).length; + } + function f(uint256[] calldata x, uint256 s, uint256 e, uint256 ss, uint256 ee) external returns (uint256) { + return uint256[](x[s:e][ss:ee]).length; + } + function f_s_only(uint256[] calldata x, uint256 s) external returns (uint256) { + return uint256[](x[s:]).length; + } + function f_e_only(uint256[] calldata x, uint256 e) external returns (uint256) { + return uint256[](x[:e]).length; + } + function g(uint256[] calldata x, uint256 s, uint256 e, uint256 idx) external returns (uint256) { + return uint256[](x[s:e])[idx]; + } + function gg(uint256[] calldata x, uint256 s, uint256 e, uint256 idx) external returns (uint256) { + return x[s:e][idx]; + } + function gg_s_only(uint256[] calldata x, uint256 s, uint256 idx) external returns (uint256) { + return x[s:][idx]; + } + function gg_e_only(uint256[] calldata x, uint256 e, uint256 idx) external returns (uint256) { + return x[:e][idx]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[],uint256,uint256): 0x60, 2, 4, 5, 1, 2, 3, 4, 5 -> 2 +// f(uint256[],uint256,uint256): 0x60, 2, 6, 5, 1, 2, 3, 4, 5 -> FAILURE +// f(uint256[],uint256,uint256): 0x60, 3, 3, 5, 1, 2, 3, 4, 5 -> 0 +// f(uint256[],uint256,uint256): 0x60, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE +// f(uint256[],uint256,uint256): 0x60, 0, 3, 5, 1, 2, 3, 4, 5 -> 3 +// f(uint256[],uint256,uint256,uint256,uint256): 0xA0, 1, 3, 1, 2, 5, 1, 2, 3, 4, 5 -> 1 +// f(uint256[],uint256,uint256,uint256,uint256): 0xA0, 1, 3, 1, 4, 5, 1, 2, 3, 4, 5 -> FAILURE +// f_s_only(uint256[],uint256): 0x40, 2, 5, 1, 2, 3, 4, 5 -> 3 +// f_s_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE +// f_e_only(uint256[],uint256): 0x40, 3, 5, 1, 2, 3, 4, 5 -> 3 +// f_e_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4 +// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE +// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4 +// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol b/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol index d8defa17a..6340c71c3 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // f(uint256,uint256): 10, 3 -> 1 // f(uint256,uint256): 10, 2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol b/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol index 9164bf06c..d93e18d06 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // f(int256,int256): 10, 3 -> 1 // f(int256,int256): 10, 2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/function_pointers.sol b/test/libsolidity/semanticTests/viaYul/function_pointers.sol new file mode 100644 index 000000000..5bd433a3d --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/function_pointers.sol @@ -0,0 +1,25 @@ +contract C { + function f() public { + function() internal returns (uint) _f; + _f(); + } + function g() public { + function() external returns (uint) _g; + _g(); + } + function h1() internal returns (function() internal returns (uint) _h) {} + function h2() public { + h1()(); + } + function k1() internal returns (function() external returns (uint) _k) {} + function k2() public { + k1()(); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE +// g() -> FAILURE +// h2() -> FAILURE +// k2() -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/keccak.sol b/test/libsolidity/semanticTests/viaYul/keccak.sol index 887faf9e4..58c37ebd6 100644 --- a/test/libsolidity/semanticTests/viaYul/keccak.sol +++ b/test/libsolidity/semanticTests/viaYul/keccak.sol @@ -8,7 +8,7 @@ contract C { } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // keccak1() -> 0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107 // keccak2() -> 0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107 diff --git a/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol b/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol new file mode 100644 index 000000000..880522d60 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol @@ -0,0 +1,10 @@ +contract C { + mapping (string => uint) map; + function set(string memory s) public { + map[s]; + } +} +// ==== +// compileViaYul: true +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> diff --git a/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol new file mode 100644 index 000000000..d0085dec1 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol @@ -0,0 +1,16 @@ +contract C { + uint[] arr1; + uint[][] arr2; + function f() internal returns (uint[] storage ptr1, uint[][] storage ptr2) { + ptr1 = arr1; + ptr2 = arr2; + } + function g() public returns (uint, uint) { + return (arr1.length, arr2.length); + } + +} +// ==== +// compileViaYul: also +// ---- +// g() -> 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/string_format.sol b/test/libsolidity/semanticTests/viaYul/string_format.sol index 2d9d71d7c..98e4d1081 100644 --- a/test/libsolidity/semanticTests/viaYul/string_format.sol +++ b/test/libsolidity/semanticTests/viaYul/string_format.sol @@ -5,7 +5,7 @@ contract C { function h() external pure returns (bytes4) { return 0xcafecafe; } } // ==== -// compileViaYul: only +// compileViaYul: true // ---- // f1() -> 0x20, 6, left(0x616263616263) // f2() -> 32, 47, 44048183223289766195424279195050628400112610419087780792899004030957505095210, 18165586057823232067963737336409268114628061002662705707816940456850361417728 diff --git a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol new file mode 100644 index 000000000..33d151f82 --- /dev/null +++ b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol @@ -0,0 +1,22 @@ +contract Base { + function f() public returns (uint256 i) { + return g(); + } + + function g() public virtual returns (uint256 i) { + return 1; + } +} + + +contract Derived is Base { + function g() public override returns (uint256 i) { + return 2; + } +} + +// ==== +// compileViaYul: also +// ---- +// g() -> 2 +// f() -> 2 diff --git a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol new file mode 100644 index 000000000..590294f15 --- /dev/null +++ b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_usage_in_constructor_arguments.sol @@ -0,0 +1,32 @@ +contract BaseBase { + uint256 m_a; + + constructor(uint256 a) public { + m_a = a; + } + + function overridden() public virtual returns (uint256 r) { + return 1; + } + + function g() public returns (uint256 r) { + return overridden(); + } +} + + +contract Base is BaseBase(BaseBase.g()) {} + + +contract Derived is Base { + function getA() public returns (uint256 r) { + return m_a; + } + + function overridden() public override returns (uint256 r) { + return 2; + } +} + +// ---- +// getA() -> 2 diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol index f49a572cf..290d73872 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol @@ -6,23 +6,17 @@ contract c { x = x + 1; return x; } - function g(bool a) public returns (bool) { + function g() public returns (bool) { bool b; - if (a) { - x = 0; - b = (f() == 0) && (f() == 0); - assert(x == 1); - assert(!b); - } else { x = 100; - b = (f() > 0) && (f() > 0); + b = f() > 0; assert(x == 102); // Should fail. assert(!b); - } return b; } } // ---- // Warning: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning: (362-372): Assertion violation happens here +// Warning: (202-218): Assertion violation happens here +// Warning: (242-252): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index 79acf49d7..f6e32d10a 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -19,4 +19,6 @@ contract A is B { } } // ---- +// Warning: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (265-270): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (253-271): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol index 5b339e004..79f0c03dc 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -26,4 +26,6 @@ contract A is B2, B1 { } // ---- // Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (342-347): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (330-348): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol index c0bf299f8..d33247a0e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -26,4 +26,6 @@ contract A is B2, B1 { } // ---- // Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (214-219): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (342-347): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (330-348): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol index 4ad9083fa..b5428d15b 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -31,4 +31,7 @@ contract A is B2, B1 { // Warning: (174-179): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (262-267): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (262-267): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (174-179): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (362-378): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol index 6959f6c88..b6363f8ef 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol @@ -26,4 +26,5 @@ contract A is B { } // ---- // Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (356-370): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol index a607a9556..c4dbe0219 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol @@ -14,3 +14,4 @@ contract A is C { } // ---- // Warning: (148-162): Assertion violation happens here +// Warning: (166-182): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol index ee4504287..1f002796f 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_call_does_not_clear_local_vars.sol @@ -9,5 +9,3 @@ contract C { } } // ---- -// Warning: (99-107): Assertion checker does not support recursive function calls. -// Warning: (141-144): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol b/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol index 2647e0771..dd3924e29 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_inline_chain.sol @@ -2,40 +2,22 @@ pragma experimental SMTChecker; contract C { - uint x; uint y; - uint z; function f() public { - if (x == 1) - x = 2; - else - x = 1; - g(); + if (y != 1) + g(); assert(y == 1); } - function g() public { + function g() internal { y = 1; h(); - assert(z == 1); } - function h() public { - z = 1; - x = 1; + function h() internal { f(); - // This fails for the following calls to the contract: - // h() - // g() h() - // It does not fail for f() g() h() because in that case - // h() will not inline f() since it already is in the callstack. - assert(x == 1); + assert(y == 1); } } // ---- -// Warning: (271-274): Assertion checker does not support recursive function calls. -// Warning: (140-143): Assertion checker does not support recursive function calls. -// Warning: (483-497): Assertion violation happens here -// Warning: (201-204): Assertion checker does not support recursive function calls. -// Warning: (483-497): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol index d2f8ab1db..ee24f0ae5 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol @@ -14,4 +14,3 @@ contract C } // ---- -// Warning: (111-114): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol index d5b83f007..5d3292992 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol @@ -22,5 +22,4 @@ contract C } } // ---- -// Warning: (206-209): Assertion checker does not support recursive function calls. -// Warning: (111-114): Assertion checker does not support recursive function calls. +// Warning: (130-144): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol new file mode 100644 index 000000000..e17e384dd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 0); + x = 1; + } + function f() public { + assert(x == 1); + ++x; + g(); + assert(x == 1); + } + + function g() internal { + assert(x == 2); + --x; + assert(x == 1); + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (245-248): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol new file mode 100644 index 000000000..3f765ecd2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol @@ -0,0 +1,32 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 1); + x = 1; + } + function f() public { + assert(x == 2); + ++x; + g(); + assert(x == 2); + } + + function g() internal { + assert(x == 3); + --x; + assert(x == 2); + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (145-159): Assertion violation happens here +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (227-241): Assertion violation happens here +// Warning: (252-266): Assertion violation happens here +// Warning: (177-191): Assertion violation happens here +// Warning: (227-241): Assertion violation happens here +// Warning: (245-248): Underflow (resulting value less than 0) happens here +// Warning: (252-266): Assertion violation happens here +// Warning: (89-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol new file mode 100644 index 000000000..0d2a3c794 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function f() internal { + assert(x == 1); + --x; + } +} + +contract C is A { + constructor() public { + assert(x == 0); + ++x; + f(); + assert(x == 0); + } +} +// ---- +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol new file mode 100644 index 000000000..da8dec6a2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + function f() internal { + assert(x == 2); + --x; + } +} + +contract C is A { + constructor() public { + assert(x == 1); + ++x; + f(); + assert(x == 1); + } +} +// ---- +// Warning: (82-96): Assertion violation happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (82-96): Assertion violation happens here +// Warning: (100-103): Underflow (resulting value less than 0) happens here +// Warning: (155-169): Assertion violation happens here +// Warning: (82-96): Assertion violation happens here +// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol new file mode 100644 index 000000000..b052f5070 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 0); + x = 1; + } + function f() public { + assert(x == 1); + ++x; + ++x; + g(); + g(); + assert(x == 1); + } + + function g() internal { + --x; + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (170-173): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol new file mode 100644 index 000000000..cb50a931e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C{ + uint x; + constructor(uint y) public { + assert(x == 1); + x = 1; + } + function f() public { + assert(x == 2); + ++x; + ++x; + g(); + g(); + assert(x == 3); + } + + function g() internal { + --x; + } +} +// ---- +// Warning: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (145-159): Assertion violation happens here +// Warning: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (170-173): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (241-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (191-205): Assertion violation happens here +// Warning: (241-244): Underflow (resulting value less than 0) happens here +// Warning: (89-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol new file mode 100644 index 000000000..5e69f7830 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function g(uint y) public { + uint z = L.f(y); + assert(z == y); + } +} + +library L { + function f(uint x) internal returns (uint) { + return x; + } +} + +// ---- +// Warning: (131-190): Function state mutability can be restricted to pure +// Warning: (86-87): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol index 17cf6ea64..0c2a781fc 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol @@ -9,4 +9,3 @@ contract C { } // // ---- -// Warning: (126-129): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol index 22b956584..00f78df3c 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol @@ -25,4 +25,3 @@ a; // ---- // Warning: (72-90): Statement has no effect. // Warning: (96-107): Statement has no effect. -// Warning: (304-307): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol index 6959f6c88..b6363f8ef 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol @@ -26,4 +26,5 @@ contract A is B { } // ---- // Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (356-370): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol index 82d411e47..7be0676d2 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -24,4 +24,10 @@ contract A is B { // ---- // Warning: (171-176): Underflow (resulting value less than 0) happens here // Warning: (171-176): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (171-176): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (260-265): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (282-287): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (282-291): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (308-313): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (296-314): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol index fc6643a42..ff316ce47 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -23,4 +23,9 @@ contract A is B { // ---- // Warning: (171-177): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (231-236): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (171-177): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (283-289): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning: (306-311): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (294-312): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol index 91798ec0c..36c8ce551 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_function_call.sol @@ -13,5 +13,3 @@ contract C { } } // ---- -// Warning: (162-175): Assertion violation happens here -// Warning: (179-193): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol index 1fb380649..6303158f7 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol @@ -1,5 +1,8 @@ pragma experimental SMTChecker; +// This test gets different results on Linux and OSX. +// Re-enable when fixed (SMTSolvers: z3) + contract Simple { function f() public pure { uint x = 10; @@ -16,9 +19,7 @@ contract Simple { } } // ==== -// SMTSolvers: z3 +// SMTSolvers: none // ---- -// Warning: (172-187): Error trying to invoke SMT solver. // Warning: (195-209): Error trying to invoke SMT solver. -// Warning: (172-187): Assertion violation happens here // Warning: (195-209): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol index e7fde4d5a..ea1c54851 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol @@ -12,10 +12,4 @@ contract Simple { assert(y == x); } } -// ==== -// SMTSolvers: z3 // ---- -// Warning: (164-179): Error trying to invoke SMT solver. -// Warning: (187-201): Error trying to invoke SMT solver. -// Warning: (164-179): Assertion violation happens here -// Warning: (187-201): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol index 7f73f86c1..4b97a7ea0 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol @@ -5,10 +5,6 @@ contract C function f(uint x, bool b) public pure { require(x < 10); for (; x < 10; ) { - if (b) { - x = 20; - continue; - } ++x; } assert(x > 15); @@ -17,4 +13,5 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning: (185-199): Assertion violation happens here +// Warning: (66-72): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (142-156): 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 index e57c62313..11cd22d11 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol @@ -13,8 +13,6 @@ contract C assert(x > 0); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (296-309): Error trying to invoke SMT solver. // Warning: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol index 1a9216de0..0694493f5 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol @@ -14,8 +14,6 @@ contract LoopFor2 { assert(b[0] == 900); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (281-301): Assertion violation happens here // Warning: (305-324): 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 c85e7e3fb..e022e0f29 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol @@ -4,6 +4,7 @@ contract C { function f(uint x, uint y) public pure { x = 7; while ((x = y) > 0) { + --y; } assert(x == 7); } @@ -11,4 +12,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning: (216-230): Assertion violation happens here +// Warning: (224-238): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol index e9752b3a8..986e7205b 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -28,8 +28,6 @@ contract C assert(x >= 20); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (329-344): Assertion violation happens here // Warning: (380-395): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol index 4447cf6d7..71238c3be 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol @@ -26,8 +26,6 @@ contract C assert(x >= 20); } } -// ==== -// SMTSolvers: z3 // ---- // Warning: (323-338): Assertion violation happens here // Warning: (362-377): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol index 7a420c28f..702c24f4b 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,4 +21,5 @@ contract C } } // ---- +// Warning: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here // Warning: (136-149): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol index b01fe812e..16d071fdb 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions_recursive.sol @@ -17,6 +17,3 @@ contract C } } // ---- -// Warning: (86-93): Assertion checker does not support recursive function calls. -// Warning: (86-93): Assertion checker does not support recursive function calls. -// Warning: (253-266): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol index 9cd7de9e6..8a1ba5e6e 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol @@ -4,7 +4,7 @@ contract C { uint[][] a; function f(bool b) public { - require(a[2][3] == 4); + a[2][3] = 4; if (b) delete a; else @@ -13,5 +13,7 @@ contract C assert(a[1][1] == 0); } } +// ==== +// SMTSolvers: z3 // ---- -// Warning: (184-204): Assertion violation happens here +// Warning: (174-194): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol index e9ef98b26..a8ae53997 100644 --- a/test/libsolidity/smtCheckerTests/special/many.sol +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -20,6 +20,7 @@ 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 2**256 - 1) happens here // Warning: (304-332): Assertion violation happens here // Warning: (336-352): Assertion violation happens here // Warning: (356-379): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol index 1657a95c6..abee678c0 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -1,13 +1,23 @@ pragma experimental SMTChecker; contract C { + function(uint) returns (uint) a; + function(uint) returns (uint) b; function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { assert(g(2) == h(2)); assert(g == h); } + function g() public { + f(a, b); + } } // ---- -// Warning: (146-150): Assertion checker does not yet implement this type of function call. -// Warning: (154-158): Assertion checker does not yet implement this type of function call. -// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons -// Warning: (139-159): Assertion violation happens here -// Warning: (163-177): Assertion violation happens here +// Warning: (214-218): Assertion checker does not yet implement this type of function call. +// Warning: (222-226): Assertion checker does not yet implement this type of function call. +// Warning: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (207-227): Assertion violation happens here +// Warning: (231-245): Assertion violation happens here +// Warning: (214-218): Assertion checker does not yet implement this type of function call. +// Warning: (222-226): Assertion checker does not yet implement this type of function call. +// Warning: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning: (207-227): Assertion violation happens here +// Warning: (231-245): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol index b2701fdf9..d94f3204b 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -10,5 +10,4 @@ contract B { } // ---- // Warning: (162-184): Assertion violation happens here -// Warning: (136-158): Assertion violation happens here // Warning: (162-184): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol index 8cd468652..00d2965bc 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -1,4 +1,5 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { @@ -8,24 +9,21 @@ contract C uint[][] memory cc, uint8[][] memory dd, uint[][][] memory eee - ) internal pure { - require(a[0] == 2); - require(cc[0][0] == 50); - require(dd[0][0] == 10); - require(eee[0][0][0] == 50); + ) public pure { + a[0] = 2; + cc[0][0] = 50; + dd[0][0] = 10; + eee[0][0][0] = 50; b[0] = 1; - // Fails because b == a is possible. - assert(a[0] == 2); - // Fails because b == cc[0] is possible. - assert(cc[0][0] == 50); + // Fails because + // b == a is possible + // b == cc[0] is possible + // b == ee[0][0] is possible + assert(a[0] == 2 || cc[0][0] == 50 || eee[0][0][0] == 50); // Should not fail since knowledge is erased only for uint[]. assert(dd[0][0] == 10); - // Fails because b == ee[0][0] is possible. - assert(eee[0][0][0] == 50); assert(b[0] == 1); } } // ---- -// Warning: (345-362): Assertion violation happens here -// Warning: (409-431): Assertion violation happens here -// Warning: (571-597): Assertion violation happens here +// Warning: (400-457): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol index d015d1471..94e7ccfcf 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol @@ -1,11 +1,12 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { uint[] array; - function f(uint[] memory a, uint[] memory b) internal view { - require(array[0] == 42); - require(a[0] == 2); + function f(uint[] memory a, uint[] memory b) public { + array[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. @@ -15,4 +16,4 @@ contract C } } // ---- -// Warning: (314-331): Assertion violation happens here +// Warning: (321-338): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol index f6d6b1977..1ae2b9356 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol @@ -1,12 +1,13 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { uint[] array; - function f(uint[] memory a, uint[] memory b) internal view { - require(array[0] == 42); + function f(uint[] memory a, uint[] memory b) public { + array[0] = 42; uint[] storage c = array; - require(a[0] == 2); + a[0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. @@ -19,4 +20,4 @@ contract C } } // ---- -// Warning: (469-486): Assertion violation happens here +// Warning: (476-493): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol index 6461634e1..2aacc5c0d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol @@ -4,7 +4,9 @@ contract C { uint[] array; uint[][] array2d; + uint[][][][] array4d; uint8[] tinyArray; + uint8[][][] tinyArray3d; function f( uint[] storage a, uint[] storage b, @@ -12,13 +14,13 @@ contract C uint8[][] storage dd, uint[][][] storage eee ) internal { - require(a[0] == 2); - require(array[0] == 42); - require(array2d[0][0] == 42); - require(tinyArray[0] == 42); - require(cc[0][0] == 42); - require(dd[0][0] == 42); - require(eee[0][0][0] == 42); + a[0] = 2; + array[0] = 42; + array2d[0][0] = 42; + tinyArray[0] = 42; + cc[0][0] = 42; + dd[0][0] = 42; + eee[0][0][0] = 42; b[0] = 1; // Fails because b == a is possible. assert(a[0] == 2); @@ -36,10 +38,19 @@ contract C assert(eee[0][0][0] == 42); assert(b[0] == 1); } + + function g(uint a, uint b, uint c, uint d, uint e) public { + f(array2d[a], array2d[b], array4d[c][c], tinyArray3d[d], array4d[e]); + } } // ---- -// Warning: (489-506): Assertion violation happens here -// Warning: (553-575): Assertion violation happens here -// Warning: (627-654): Assertion violation happens here -// Warning: (795-817): Assertion violation happens here -// Warning: (957-983): Assertion violation happens here +// Warning: (468-485): Assertion violation happens here +// Warning: (532-554): Assertion violation happens here +// Warning: (606-633): Assertion violation happens here +// Warning: (774-796): Assertion violation happens here +// Warning: (936-962): Assertion violation happens here +// Warning: (468-485): Assertion violation happens here +// Warning: (532-554): Assertion violation happens here +// Warning: (606-633): Assertion violation happens here +// Warning: (774-796): Assertion violation happens here +// Warning: (936-962): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol index 4b134e58e..3d60c836a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol @@ -2,9 +2,14 @@ pragma experimental SMTChecker; contract C { + uint[][] array2d; + function g(uint x, uint y, uint[] memory c) public { + f(array2d[x], array2d[y], c); + } + function f(uint[] storage a, uint[] storage b, uint[] memory c) internal { - require(c[0] == 42); - require(a[0] == 2); + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage references should not // erase knowledge about memory references. @@ -15,4 +20,5 @@ contract C } } // ---- -// Warning: (347-364): Assertion violation happens here +// Warning: (436-453): Assertion violation happens here +// Warning: (436-453): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol index d70cc18bb..68438eb6d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol @@ -2,10 +2,14 @@ pragma experimental SMTChecker; contract C { + uint[][] array2d; + function g(uint x, uint y, uint[] memory c) public { + f(array2d[x], array2d[y], c); + } function f(uint[] storage a, uint[] storage b, uint[] memory c) internal { uint[] memory d = c; - require(c[0] == 42); - require(a[0] == 2); + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage references should not // erase knowledge about memory references. @@ -19,4 +23,7 @@ contract C } } // ---- -// Warning: (497-514): Assertion violation happens here +// Warning: (524-542): Assertion violation happens here +// Warning: (585-602): Assertion violation happens here +// Warning: (524-542): Assertion violation happens here +// Warning: (585-602): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol index 13dc6fcc2..384a89b4f 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol @@ -3,9 +3,10 @@ pragma experimental SMTChecker; contract C { uint[] array; + uint[][] array2d; function f(uint[] storage a, uint[] storage b) internal { - require(a[0] == 2); - require(b[0] == 42); + a[0] = 2; + b[0] = 42; array[0] = 1; // Fails because array == a is possible. assert(a[0] == 2); @@ -13,7 +14,12 @@ contract C assert(b[0] == 42); assert(array[0] == 1); } + function g(uint x, uint y) public { + f(array2d[x], array2d[y]); + } } // ---- -// Warning: (226-243): Assertion violation happens here -// Warning: (290-308): Assertion violation happens here +// Warning: (225-242): Assertion violation happens here +// Warning: (289-307): Assertion violation happens here +// Warning: (225-242): Assertion violation happens here +// Warning: (289-307): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol index ff47cc0e4..e94ad4580 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol @@ -4,20 +4,29 @@ contract C { uint[] b; uint[] d; + uint[][] array2d; + function g(uint x, uint[] memory c) public { + f(array2d[x], c); + } function f(uint[] storage a, uint[] memory c) internal { - require(d[0] == 42); - require(c[0] == 42); - require(a[0] == 2); + d[0] = 42; + c[0] = 42; + a[0] = 2; b[0] = 1; // Erasing knowledge about storage variables should not // erase knowledge about memory references. assert(c[0] == 42); - // Should not fail since b == d is not possible. + // Fails because d == a is possible. assert(d[0] == 42); - // Fails because b == a is possible. + // Fails because b == a and d == a are possible. assert(a[0] == 2); + // b == a is possible, but does not fail because b + // was the last assignment. assert(b[0] == 1); } } // ---- -// Warning: (446-463): Assertion violation happens here +// Warning: (431-449): Assertion violation happens here +// Warning: (504-521): Assertion violation happens here +// Warning: (431-449): Assertion violation happens here +// Warning: (504-521): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol index 062e9776d..dc69dd55a 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol @@ -7,9 +7,9 @@ contract C mapping (uint => uint8)[] severalMaps8; mapping (uint => uint)[][] severalMaps3d; function f(mapping (uint => uint) storage map) internal { - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; map[0] = 2; // Should fail since map == severalMaps[0] is possible. assert(severalMaps[0][0] == 42); @@ -18,7 +18,12 @@ contract C // Should fail since map == severalMaps3d[0][0] is possible. assert(severalMaps3d[0][0][0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (451-482): Assertion violation happens here -// Warning: (665-701): Assertion violation happens here +// Warning: (421-452): Assertion violation happens here +// Warning: (635-671): Assertion violation happens here +// Warning: (421-452): Assertion violation happens here +// Warning: (635-671): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol index 1d5ab2687..2e16e7b5d 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol @@ -8,9 +8,9 @@ contract C mapping (uint => uint)[][] severalMaps3d; function f(mapping (uint => uint) storage map) internal { map[0] = 42; - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; singleMap[0] = 2; // Should not fail since singleMap == severalMaps[0] is not possible. assert(severalMaps[0][0] == 42); @@ -21,6 +21,10 @@ contract C // Should fail since singleMap == map is possible. assert(map[0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (807-827): Assertion violation happens here +// Warning: (777-797): Assertion violation happens here +// Warning: (777-797): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol index 99e993032..dd846b483 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol @@ -2,7 +2,7 @@ pragma experimental SMTChecker; contract C { - function f(uint[2] memory a, uint[2] memory b, uint[2] memory c) internal pure { + function f(uint[2] memory a, uint[2] memory b, uint[2] memory c) public pure { require(c[0] == 42); require(a[0] == 2); b[0] = 1; @@ -14,5 +14,5 @@ contract C } } // ---- -// Warning: (230-248): Assertion violation happens here -// Warning: (295-312): Assertion violation happens here +// Warning: (228-246): Assertion violation happens here +// Warning: (293-310): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol index fa0c5eae1..45c12263b 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol @@ -2,18 +2,25 @@ pragma experimental SMTChecker; contract C { - uint[2] b; + uint[2] b1; + uint[2] b2; function f(uint[2] storage a, uint[2] memory c) internal { - require(c[0] == 42); - require(a[0] == 2); - b[0] = 1; + c[0] = 42; + a[0] = 2; + b1[0] = 1; // Erasing knowledge about storage variables should not // erase knowledge about memory references. assert(c[0] == 42); - // Fails because b == a is possible. + // Fails because b1 == a is possible. assert(a[0] == 2); - assert(b[0] == 1); + assert(b1[0] == 1); + } + function g(bool x, uint[2] memory c) public { + if (x) f(b1, c); + else f(b2, c); } } // ---- -// Warning: (342-359): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here +// Warning: (338-355): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol index a675c29e2..7e31b10ae 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol @@ -7,9 +7,9 @@ contract C mapping (uint => uint8)[2] severalMaps8; mapping (uint => uint)[2][2] severalMaps3d; function f(mapping (uint => uint) storage map) internal { - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; map[0] = 2; // Should fail since map == severalMaps[0] is possible. assert(severalMaps[0][0] == 42); @@ -18,7 +18,12 @@ contract C // Should fail since map == severalMaps3d[0][0] is possible. assert(severalMaps3d[0][0][0] == 42); } + function g(uint x) public { + f(severalMaps[x]); + } } // ---- -// Warning: (455-486): Assertion violation happens here -// Warning: (669-705): Assertion violation happens here +// Warning: (425-456): Assertion violation happens here +// Warning: (639-675): Assertion violation happens here +// Warning: (425-456): Assertion violation happens here +// Warning: (639-675): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol index c9c96a0a7..e2f597943 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol @@ -8,9 +8,9 @@ contract C mapping (uint => uint)[2][2] severalMaps3d; function f(mapping (uint => uint) storage map) internal { map[0] = 42; - require(severalMaps[0][0] == 42); - require(severalMaps8[0][0] == 42); - require(severalMaps3d[0][0][0] == 42); + severalMaps[0][0] = 42; + severalMaps8[0][0] = 42; + severalMaps3d[0][0][0] = 42; singleMap[0] = 2; // Should not fail since singleMap == severalMaps[0] is not possible. assert(severalMaps[0][0] == 42); @@ -21,6 +21,10 @@ contract C // Should fail since singleMap == map is possible. assert(map[0] == 42); } + function g(uint x) public { + f(severalMaps3d[x][0]); + } } // ---- -// Warning: (811-831): Assertion violation happens here +// Warning: (781-801): Assertion violation happens here +// Warning: (781-801): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library.sol index a1648c66e..9c1c3caaa 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library.sol @@ -8,7 +8,7 @@ library L contract C { enum E { Left, Right } - function f(E _d) internal pure { + function f(E _d) public pure { _d = E.Left; assert(_d == E.Left); } diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol index 2bd2afbda..be1d40be4 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol @@ -8,10 +8,10 @@ library L contract C { enum E { Left, Right } - function f(E _d) internal pure { + function f(E _d) public pure { _d = E.Right; assert(_d == E.Left); } } // ---- -// Warning: (161-181): Assertion violation happens here +// Warning: (159-179): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol index 47bdd0c66..4821518fd 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol @@ -1,19 +1,20 @@ pragma experimental SMTChecker; +pragma experimental ABIEncoderV2; contract C { enum D { Left, Right } struct S { uint x; D d; } - function f(S memory s) internal pure { + function f(S memory s) public pure { s.d = D.Left; assert(s.d == D.Left); } } // ---- -// Warning: (109-119): Assertion checker does not yet support the type of this variable. -// Warning: (139-142): Assertion checker does not yet support this expression. -// Warning: (139-140): Assertion checker does not yet implement type struct C.S memory -// Warning: (139-151): Assertion checker does not yet implement such assignments. -// Warning: (162-165): Assertion checker does not yet support this expression. -// Warning: (162-163): Assertion checker does not yet implement type struct C.S memory -// Warning: (155-176): Assertion violation happens here +// Warning: (143-153): Assertion checker does not yet support the type of this variable. +// Warning: (171-174): Assertion checker does not yet support this expression. +// Warning: (171-172): Assertion checker does not yet implement type struct C.S memory +// Warning: (171-183): Assertion checker does not yet implement such assignments. +// Warning: (194-197): Assertion checker does not yet support this expression. +// Warning: (194-195): Assertion checker does not yet implement type struct C.S memory +// Warning: (187-208): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol new file mode 100644 index 000000000..bb2513790 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C +{ + bytes32 x; + function f(bytes8 y) public view { + assert(x == g()); + assert(x != y); + } + function g() public view returns (bytes32) { + return x; + } +} +// ---- +// Warning: (116-130): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol index 39d096f59..8170d67e6 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol @@ -5,8 +5,9 @@ contract C mapping (uint => uint) a; mapping (uint => uint) b; - function f() public { - require(a[1] == b[1]); + function f(uint x) public { + a[1] = x; + b[1] = x; a[1] = 2; mapping (uint => uint) storage c = a; assert(c[1] == 2); @@ -15,4 +16,4 @@ contract C } } // ---- -// Warning: (261-281): Assertion violation happens here +// Warning: (266-286): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol index 86af187ad..f31192b3a 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol @@ -6,10 +6,10 @@ contract C mapping (uint => mapping (uint => uint)) maps; mapping (uint => mapping (uint => uint8)) maps8; function f(mapping (uint => uint) storage map1, mapping (uint => uint) storage map2) internal { - require(map1[0] == 2); - require(a[0] == 42); - require(maps[0][0] == 42); - require(maps8[0][0] == 42); + map1[0] = 2; + a[0] = 42; + maps[0][0] = 42; + maps8[0][0] = 42; map2[0] = 1; // Fails because map2 == map1 is possible. assert(map1[0] == 2); @@ -21,8 +21,21 @@ contract C assert(maps8[0][0] == 42); assert(map2[0] == 1); } + + function g(bool b, uint x, uint y) public { + if (b) + f(a, maps[y]); + else + f(maps[x], maps[y]); + } } // ---- -// Warning: (437-457): Assertion violation happens here -// Warning: (503-521): Assertion violation happens here -// Warning: (573-597): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here +// Warning: (397-417): Assertion violation happens here +// Warning: (463-481): Assertion violation happens here +// Warning: (533-557): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol index 4a9291f89..afb9a9ea0 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol @@ -11,3 +11,4 @@ contract C { } // ---- // Warning: (147-166): Assertion violation happens here +// Warning: (170-190): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function.sol b/test/libsolidity/smtCheckerTests/types/tuple_function.sol index 53b197395..8103a0203 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function.sol @@ -15,3 +15,4 @@ contract C } // ---- // Warning: (182-196): Assertion violation happens here +// Warning: (200-214): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol index 1e548403d..e76db56d1 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol @@ -17,3 +17,4 @@ contract C } // ---- // Warning: (205-219): Assertion violation happens here +// Warning: (223-237): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTestsJSON/multi.json b/test/libsolidity/smtCheckerTestsJSON/multi.json index b125712fd..b1be75073 100644 --- a/test/libsolidity/smtCheckerTestsJSON/multi.json +++ b/test/libsolidity/smtCheckerTestsJSON/multi.json @@ -3,7 +3,7 @@ { "smtlib2responses": { - "0x82fb8ee094f0f56b7a63a74177b54a1710d6fc531d426f288c18f36b76cf6a8b": "sat\n((|EVALEXPR_0| 1))\n", + "0x9c50514d749eabf3c13d97ad7d787e682dd99a114bad652b10a01b8c6ad6c1fb": "sat\n((|EVALEXPR_0| 1))\n", "0xb524e7c577188e2e36f0e67fead51269fa0f8b8fb41bff2d973dcf584d38cd1e": "sat\n((|EVALEXPR_0| 0))\n" } } diff --git a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol index f3be90715..4966b4e85 100644 --- a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol +++ b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol @@ -2,8 +2,10 @@ contract C { function f() public { uint[] storage x; uint[10] storage y; + x; + y; } } // ---- -// DeclarationError: (38-54): Uninitialized storage pointer. -// DeclarationError: (58-76): Uninitialized storage pointer. +// TypeError: (80-81): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (85-86): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol new file mode 100644 index 000000000..ddb5faa9f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol @@ -0,0 +1,38 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + for {} eq(0,0) { c_slot := s_slot } {} + } + c; + } + function g() internal pure { + S storage c; + assembly { + for {} eq(0,1) { c_slot := s_slot } {} + } + c; + } + function h() internal pure { + S storage c; + assembly { + for {} eq(0,0) {} { c_slot := s_slot } + } + c; + } + function i() internal pure { + S storage c; + assembly { + for {} eq(0,1) {} { c_slot := s_slot } + } + c; + } +} +// ---- +// TypeError: (189-190): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (340-341): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (491-492): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (642-643): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. + diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol new file mode 100644 index 000000000..19a58b234 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol @@ -0,0 +1,19 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + for { c_slot := s_slot } iszero(0) {} {} + } + c; + } + function g() internal pure { + S storage c; + assembly { + for { c_slot := s_slot } iszero(1) {} {} + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol new file mode 100644 index 000000000..1d4302755 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal pure { + S storage c; + assembly { + if flag { c_slot := s_slot } + } + c; + } +} +// ---- +// TypeError: (188-189): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol new file mode 100644 index 000000000..180feb770 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + // this should warn about unreachable code, but currently function flow is ignored + assembly { + function f() { return(0, 0) } + f() + c_slot := s_slot + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol new file mode 100644 index 000000000..bba9daf34 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + // this could be allowed, but currently control flow for functions is not analysed + assembly { + function f() { revert(0, 0) } + f() + } + c; + } +} +// ---- +// TypeError: (287-288): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol new file mode 100644 index 000000000..c3f240915 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol @@ -0,0 +1,12 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + c_slot := s_slot + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol new file mode 100644 index 000000000..72884e741 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol @@ -0,0 +1,33 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { c_slot := s_slot } + } + c; + } + function g(bool flag) internal pure { + S storage c; + assembly { + switch flag + case 0 { c_slot := s_slot } + case 1 { c_slot := s_slot } + } + c; + } + function h(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { c_slot := s_slot } + default { return(0,0) } + } + c; + } +} +// ---- +// TypeError: (208-209): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (421-422): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol new file mode 100644 index 000000000..d72310924 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol @@ -0,0 +1,31 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure { + S storage c; + assembly { + switch a + default { c_slot := s_slot } + } + c; + } + function g(bool flag) internal pure { + S storage c; + assembly { + switch flag + case 0 { c_slot := s_slot } + default { c_slot := s_slot } + } + c; + } + function h(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { revert(0, 0) } + default { c_slot := s_slot } + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol new file mode 100644 index 000000000..25654b1dd --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol @@ -0,0 +1,66 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + do { + break; + c = s; + } while(false); + c; + } + function g() internal view { + S storage c; + do { + if (s.f) { + continue; + c = s; + } + else { + } + } while(false); + c; + } + function h() internal view { + S storage c; + do { + if (s.f) { + break; + } + else { + c = s; + } + } while(false); + c; + } + function i() internal view { + S storage c; + do { + if (s.f) { + continue; + } + else { + c = s; + } + } while(false); + c; + } + function j() internal view { + S storage c; + do { + continue; + c = s; + } while(false); + c; + } +} +// ---- +// TypeError: (184-185): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (145-150): Unreachable code. +// Warning: (168-173): Unreachable code. +// TypeError: (411-412): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (325-330): Unreachable code. +// TypeError: (635-636): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (862-863): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (1011-1012): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (972-977): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol new file mode 100644 index 000000000..40ddc3777 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol @@ -0,0 +1,44 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + do {} while((c = s).f); + c; + } + function g() internal view { + S storage c; + do { c = s; } while(false); + c; + } + function h() internal view { + S storage c; + c = s; + do {} while(false); + c; + } + function i() internal view { + S storage c; + do {} while(false); + c = s; + c; + } + function j() internal view { + S storage c; + do { + c = s; + break; + } while(false); + c; + } + function k() internal view { + S storage c; + do { + c = s; + continue; + } while(false); + c; + } +} +// ---- +// Warning: (606-611): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol new file mode 100644 index 000000000..e93d9798f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol @@ -0,0 +1,20 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + for(;; c = s) { + } + c; + } + function g() internal view { + S storage c; + for(;;) { + c = s; + } + c; + } +} +// ---- +//TypeError: (143-144): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (261-262): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol new file mode 100644 index 000000000..b3e9f46d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + for(c = s;;) { + } + c; + } + function g() internal view { + S storage c; + for(; (c = s).f;) { + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol new file mode 100644 index 000000000..7da6bef92 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol @@ -0,0 +1,22 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal { + S storage c; + if (flag) c = s; + c; + } + function g(bool flag) internal { + S storage c; + if (flag) c = s; + else + { + if (!flag) c = s; + else s.f = true; + } + c; + } +} +// ---- +// TypeError: (138-139): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (330-331): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol new file mode 100644 index 000000000..5d3a6d5bd --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol @@ -0,0 +1,39 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + if (flag) c = s; + else c = s; + c; + } + function g(bool flag) internal view { + S storage c; + if (flag) c = s; + else { c = s; } + c; + } + function h(bool flag) internal view { + S storage c; + if (flag) c = s; + else + { + if (!flag) c = s; + else c = s; + } + c; + } + function i() internal view { + S storage c; + if ((c = s).f) { + } + c; + } + function j() internal view { + S storage c; + if ((c = s).f && !(c = s).f) { + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol new file mode 100644 index 000000000..6df4939cf --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol @@ -0,0 +1,20 @@ +contract C { + modifier revertIfNoReturn() { + _; + revert(); + } + modifier ifFlag(bool flag) { + if (flag) + _; + } + struct S { uint a; } + S s; + function f(bool flag) revertIfNoReturn() internal view { + if (flag) s; + } + function g(bool flag) revertIfNoReturn() ifFlag(flag) internal view { + s; + } + +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol new file mode 100644 index 000000000..5cb580528 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol @@ -0,0 +1,11 @@ +contract C { + struct S { bool f; } + S s; + function g(bool flag) internal view { + S storage c; + if (flag) c = s; + else revert(); + s; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol new file mode 100644 index 000000000..a6dd300ce --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol @@ -0,0 +1,24 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + false && (c = s).f; + c; + } + function g() internal view { + S storage c; + true || (c = s).f; + c; + } + function h() internal view { + S storage c; + // expect error, although this is always fine + true && (false || (c = s).f); + c; + } +} +// ---- +// TypeError: (137-138): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (235-236): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (398-399): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol new file mode 100644 index 000000000..e8ad5e485 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + (c = s).f && false; + c; + } + function g() internal view { + S storage c; + (c = s).f || true; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol new file mode 100644 index 000000000..f820456e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure {} + function g() internal view { s; } + function h() internal view { + S storage c; + c = s; + c; + } + function i() internal view { + S storage c; + (c) = s; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol new file mode 100644 index 000000000..2c47731aa --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol @@ -0,0 +1,11 @@ +contract C { + uint256[] s; + function f() public { + bool d; + uint256[] storage x; + uint256[] storage y = d ? (x = s) : x; + y; + } +} +// ---- +// TypeError: (145-146): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol new file mode 100644 index 000000000..a510bd40d --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol @@ -0,0 +1,9 @@ +contract C { + uint256[] s; + function f() public view { + uint256[] storage x; + uint256[] storage y = (x = s)[0] > 0 ? x : x; + y; + } +} +// --- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol new file mode 100644 index 000000000..3b4552f86 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + flag ? (c = s).f : false; + c; + } + function g(bool flag) internal view { + S storage c; + flag ? false : (c = s).f; + c; + } +} +// ---- +// TypeError: (152-153): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (266-267): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol new file mode 100644 index 000000000..1dd8d2f57 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol @@ -0,0 +1,20 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + flag ? c = s : c = s; + c; + } + function g(bool flag) internal view { + S storage c; + flag ? c = s : (c = s); + c; + } + function h(bool flag) internal view { + S storage c; + flag ? (c = s).f : (c = s).f; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol new file mode 100644 index 000000000..ca43dc346 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol @@ -0,0 +1,42 @@ +contract C { + struct S { bool f; } + S s; + function ext() external {} + function f() internal + { + S storage r; + try this.ext() { } + catch (bytes memory) { r = s; } + r; + } + function g() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { } + r; + } + function h() internal + { + S storage r; + try this.ext() {} + catch Error (string memory) { r = s; } + catch (bytes memory) { r = s; } + r; + } + function i() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { r; } + r = s; + r; + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError: (206-207): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (343-344): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (526-527): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (653-654): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol new file mode 100644 index 000000000..6834525d5 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol @@ -0,0 +1,30 @@ +contract C { + struct S { bool f; } + S s; + function ext() external { } + function f() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { r = s; } + r; + } + function g() internal + { + S storage r; + try this.ext() { r = s; } + catch Error (string memory) { r = s; } + catch (bytes memory) { r = s; } + r; + } + function h() internal + { + S storage r; + try this.ext() { } + catch (bytes memory) { } + r = s; + r; + } +} +// ==== +// EVMVersion: >=byzantium diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol new file mode 100644 index 000000000..e2e274196 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage, uint) { + return (s,2); + } + function g() internal view { + uint a; + S storage c; + (c, a) = f(); + c; + } + function h() internal view { + (s, s); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol new file mode 100644 index 000000000..cd6fee8db --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + while(false) { + c = s; + } + c; + } +} +// ---- +// TypeError: (161-162): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol new file mode 100644 index 000000000..20e72e5db --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol @@ -0,0 +1,25 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + while((c = s).f) { + } + c; + } + function g() internal view { + S storage c; + c = s; + while(false) { + } + c; + } + function h() internal view { + S storage c; + while(false) { + } + c = s; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/as_function_param.sol b/test/libsolidity/syntaxTests/immutable/as_function_param.sol new file mode 100644 index 000000000..3636b0aef --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/as_function_param.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint immutable) public pure {} +} +// ---- +// DeclarationError: (28-42): The "immutable" keyword can only be used for state variables. diff --git a/test/libsolidity/syntaxTests/immutable/assembly.sol b/test/libsolidity/syntaxTests/immutable/assembly.sol new file mode 100644 index 000000000..7b98b67c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/assembly.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + function f() public view { + uint t; + assembly { + t := x + } + } +} +// ---- +// TypeError: (118-119): Assembly access to immutable variables is not supported. diff --git a/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol new file mode 100644 index 000000000..702f03079 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + constructor() public { + if (false) + return; + + x = 1; + } +} +// ---- +// TypeError: (93-100): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol new file mode 100644 index 000000000..b8557321b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + if (false) + x = 1; + } +} +// ---- +// TypeError: (93-94): Immutable variables must be initialized unconditionally, not in an if statement. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol new file mode 100644 index 000000000..1423659c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() public { + initX(); + } + + function initX() internal { + x = 3; + } +} +// ---- +// TypeError: (126-127): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol new file mode 100644 index 000000000..ad17635c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x; + constructor() public { + x = f(); + } + + function f() public pure returns (uint) { return 3 + x; } +} +// ---- +// TypeError: (143-144): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol new file mode 100644 index 000000000..0b2207808 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x; + constructor() public { + x = 3 + x; + } +} +// ---- +// TypeError: (78-79): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol new file mode 100644 index 000000000..f3b724f03 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol @@ -0,0 +1,13 @@ +contract C { + uint immutable x; + uint immutable y; + constructor() public { + (x, y) = f(); + } + + function f() internal pure returns(uint _x, uint _y) { + _x = 3; + _y = 4; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol new file mode 100644 index 000000000..2c1c37c79 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() readX(x = 3) public { } + + modifier readX(uint _x) { + _; f(_x); + } + + function f(uint a) internal pure {} +} +// ---- +// TypeError: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol new file mode 100644 index 000000000..b26666530 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + constructor() initX public { + } + + modifier initX() { + _; x = 23; + } +} +// ---- +// TypeError: (109-110): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol new file mode 100644 index 000000000..a9a1b3a22 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol @@ -0,0 +1,14 @@ +contract C { + uint immutable x; + constructor() readX public { + x = 3; + } + + modifier readX() { + _; f(x); + } + + function f(uint a) internal pure {} +} +// ---- +// TypeError: (126-127): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol new file mode 100644 index 000000000..54ac9f615 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/decrement.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + x--; + } +} +// ---- +// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete.sol b/test/libsolidity/syntaxTests/immutable/delete.sol new file mode 100644 index 000000000..def28fad2 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/delete.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + delete x; + } +} +// ---- +// TypeError: (81-82): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/double_specifier.sol b/test/libsolidity/syntaxTests/immutable/double_specifier.sol new file mode 100644 index 000000000..39f0f1e76 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/double_specifier.sol @@ -0,0 +1,7 @@ +contract C { + uint immutable immutable x; + uint immutable constant x; +} +// ---- +// ParserError: (32-41): Constantness already set to "immutable" +// ParserError: (64-72): Constantness already set to "immutable" diff --git a/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol new file mode 100644 index 000000000..7ab8393cf --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol @@ -0,0 +1,5 @@ +contract C { + function() external immutable f; +} +// ---- +// TypeError: (17-48): Immutable variables of external function type are not yet supported. diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization.sol b/test/libsolidity/syntaxTests/immutable/function_initialization.sol new file mode 100644 index 000000000..c836beb27 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_initialization.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable x = f(); + + function f() public pure returns (uint) { return 3; } +} diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol new file mode 100644 index 000000000..2a4365e69 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol @@ -0,0 +1,7 @@ +contract C { + uint immutable x = f(); + + function f() public pure returns (uint) { return 3 + x; } +} +// ---- +// TypeError: (99-100): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol new file mode 100644 index 000000000..04eaa621d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol @@ -0,0 +1,14 @@ +contract B { + uint immutable x; + + constructor(function() internal returns(uint) fp) internal { + x = fp(); + } +} + +contract C is B(C.f) { + function f() internal returns(uint) { return x = 2; } +} +// ---- +// TypeError: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (200-201): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol new file mode 100644 index 000000000..2ec62e931 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol @@ -0,0 +1,13 @@ +contract B { + uint immutable x; + + constructor(function() internal returns(uint) fp) internal { + x = fp(); + } +} + +contract C is B(C.f) { + function f() internal returns(uint) { return x + 2; } +} +// ---- +// TypeError: (200-201): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/immutable_basic.sol b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol new file mode 100644 index 000000000..3d8e04636 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol @@ -0,0 +1,3 @@ +contract C { + uint immutable x = 0; +} diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol new file mode 100644 index 000000000..0068bf87d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/increment.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + x++; + } +} +// ---- +// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol new file mode 100644 index 000000000..5f659bff6 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 0; + uint y = f(); + + function f() internal returns(uint) { return x; } +} +// ---- +// TypeError: (107-108): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol new file mode 100644 index 000000000..ee794fd37 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol @@ -0,0 +1,15 @@ +contract B { + uint immutable x; + + constructor() public { + x = 3; + } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol new file mode 100644 index 000000000..0d98f8b40 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol @@ -0,0 +1,14 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B { + uint immutable y; + constructor() B(y = 3) public { } +} +// ---- +// TypeError: (155-156): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol new file mode 100644 index 000000000..16ed52f36 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol @@ -0,0 +1,13 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B(C.y = 3) { + uint immutable y; +} +// ---- +// TypeError: (111-114): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol new file mode 100644 index 000000000..ed352746f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol @@ -0,0 +1,16 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B(C.y) { + uint immutable y; + constructor() public { + y = 3; + } +} +// ---- +// TypeError: (111-114): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol new file mode 100644 index 000000000..d56fb487a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return 3; + } +} + +contract C is B { + function xInit() internal override returns(uint) { + return x; + } +} +// ---- +// TypeError: (260-261): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol new file mode 100644 index 000000000..5dda6bfb0 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x = 3; + + function readX() internal virtual returns(uint) { + return x; + } +} + +contract C is B { + constructor() public { + B.readX; + } + + function readX() internal override returns(uint) { + return 3; + } +} +// ---- +// TypeError: (109-110): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol new file mode 100644 index 000000000..278efe55f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x = 3; + + function readX() internal view virtual returns(uint) { + return x; + } +} + +contract C is B { + constructor() public { + super.readX(); + } + + function readX() internal view override returns(uint) { + return 1; + } +} +// ---- +// TypeError: (114-115): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol new file mode 100644 index 000000000..f1e60dbd8 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol @@ -0,0 +1,21 @@ +contract B { + uint immutable x; + + constructor() readX public { + x = 3; + } + + modifier readX() virtual { + _; f(3); + } + + function f(uint a) internal pure {} +} + +contract C is B { + modifier readX() override { + _; f(x); + } +} +// ---- +// TypeError: (252-253): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol new file mode 100644 index 000000000..24a5239ed --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol @@ -0,0 +1,12 @@ +contract B { + uint immutable x = 4; +} + +contract C is B { + constructor() public { + x = 3; + } +} +// ---- +// TypeError: (95-96): Immutable variables must be initialized in the constructor of the contract they are defined in. +// TypeError: (95-96): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol new file mode 100644 index 000000000..a6db6a665 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol @@ -0,0 +1,8 @@ +contract C { + constructor() public { + return; + } + + uint immutable x = 3; +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol new file mode 100644 index 000000000..2827ab389 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + while (true) + x = 1; + } +} +// ---- +// TypeError: (95-96): Immutable variables can only be initialized once, not in a while statement. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol new file mode 100644 index 000000000..c71335b83 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol @@ -0,0 +1,29 @@ +contract A { + function f() internal virtual returns(uint) { return 3; } +} + +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return f(); + } + + function f() internal virtual returns(uint) { return 3; } +} + +contract C is A, B { + function xInit() internal override returns(uint) { + return B.xInit(); + } + + function f() internal override(A, B) returns(uint) { + return x; + } +} +// ---- +// TypeError: (496-497): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol new file mode 100644 index 000000000..eaabbb71a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol @@ -0,0 +1,29 @@ +contract A { + function f() internal virtual returns(uint) { return 3; } +} + +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return f(); + } + + function f() internal virtual returns(uint) { return 3; } +} + +contract C is A, B { + function xInit() internal override returns(uint) { + return super.xInit(); + } + + function f() internal override(A, B) returns(uint) { + return x; + } +} +// ---- +// TypeError: (500-501): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol new file mode 100644 index 000000000..8ff940fef --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + x = 1; + x = 4; + } +} +// ---- +// TypeError: (85-86): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/non-value_type.sol b/test/libsolidity/syntaxTests/immutable/non-value_type.sol new file mode 100644 index 000000000..67398ce20 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/non-value_type.sol @@ -0,0 +1,5 @@ +contract C { + uint[] immutable x; +} +// ---- +// TypeError: (17-35): Immutable variables cannot have a non-value type. diff --git a/test/libsolidity/syntaxTests/immutable/private_state_var.sol b/test/libsolidity/syntaxTests/immutable/private_state_var.sol new file mode 100644 index 000000000..2cfc22a85 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/private_state_var.sol @@ -0,0 +1,20 @@ +contract B { + uint immutable private x = f(); + + constructor() public { + } + + function f() internal view virtual returns(uint) { return 1; } + function readX() internal view returns(uint) { return x; } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } + function f() internal view override returns(uint) { return readX(); } + +} +// ---- +// TypeError: (209-210): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol new file mode 100644 index 000000000..b64f236a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 0; + uint y = 0; + + function f() internal { + y = x + 1; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol new file mode 100644 index 000000000..4de778ba3 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x = 0; + uint y = 0; + + function f() readX internal { + } + + modifier readX() { + _; + y = x + 1; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol new file mode 100644 index 000000000..d35c68d8a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol @@ -0,0 +1,6 @@ +contract C { + uint immutable x = 0; + uint y = x; +} +// ---- +// TypeError: (52-53): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol new file mode 100644 index 000000000..0fd5461d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x; + constructor() public { + return; + + x = 1; + } +} +// ---- +// TypeError: (70-77): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/selector.sol b/test/libsolidity/syntaxTests/immutable/selector.sol new file mode 100644 index 000000000..4b880c8e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + this.readX.selector; + } + + function readX() external view returns(uint) { return x; } +} +// ---- +// Warning: (85-104): Statement has no effect. +// Warning: (85-89): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_name.sol b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol new file mode 100644 index 000000000..c84f72408 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol @@ -0,0 +1,13 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + C.selector.selector; + C.selector; + } + + function selector() external view returns(uint) { return x; } +} +// ---- +// Warning: (85-104): Statement has no effect. +// Warning: (114-124): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol new file mode 100644 index 000000000..a8b91a639 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol @@ -0,0 +1,16 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + readX().selector; + } + + function f() external view returns(uint) { + return x; + } + + function readX() public view returns(function() external view returns(uint) _f) { + _f = this.f; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized.sol b/test/libsolidity/syntaxTests/immutable/uninitialized.sol new file mode 100644 index 000000000..18c60ea9d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/uninitialized.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable x; +} +// ---- +// TypeError: (0-36): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol new file mode 100644 index 000000000..69aa5448f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol @@ -0,0 +1,21 @@ +contract B { + uint immutable private x; + + constructor() public { + } + + function f() internal view virtual returns(uint) { return 1; } + function readX() internal view returns(uint) { return x; } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } + function f() internal view override returns(uint) { return readX(); } + +} +// ---- +// TypeError: (0-209): Construction control flow ends without initializing all immutable state variables. +// TypeError: (211-375): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol new file mode 100644 index 000000000..2b5614650 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 1; + + function readX() internal view returns(uint) { + return x + 3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol new file mode 100644 index 000000000..e844de080 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x = 0; + + function f() internal { + x = 1; + } +} +// ---- +// TypeError: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (76-77): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol new file mode 100644 index 000000000..592633379 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x = 0; + + function f() readX internal { } + + modifier readX() { + _; x = 1; + } +} +// ---- +// TypeError: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (111-112): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol new file mode 100644 index 000000000..d02284aef --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol @@ -0,0 +1,12 @@ +pragma experimental ABIEncoderV2; + +abstract contract C { + struct S { + uint256 x; + string y; + } + function f(address x) external virtual returns (uint256, string memory); +} +contract D is C { + mapping(address => S) public override f; +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol new file mode 100644 index 000000000..4e6e02598 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol @@ -0,0 +1,13 @@ +pragma experimental ABIEncoderV2; + +abstract contract C { + struct S { + uint256 x; + string y; + } + + function f() external virtual returns (uint256, string memory); +} +contract D is C { + S public override f; +} diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol index ecda3e994..625502f25 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol @@ -5,3 +5,5 @@ contract C { } } } +// ---- +// DeclarationError: (72-73): Access to functions is not allowed in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol new file mode 100644 index 000000000..2fc49b1db --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure {} + constructor() public { + assembly { + let x := f + } + } +} +// ---- +// DeclarationError: (112-113): Access to functions is not allowed in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/const_from_non_const.sol b/test/libsolidity/syntaxTests/inlineAssembly/const_from_non_const.sol new file mode 100644 index 000000000..fbd5ab6ee --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/const_from_non_const.sol @@ -0,0 +1,12 @@ +contract C { + bool nc = false; + bool constant c = nc; + function f() public { + assembly { + let t := c + } + } +} +// ---- +// TypeError: (52-54): Initial value for constant variable has to be compile-time constant. +// TypeError: (112-113): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/const_from_this.sol b/test/libsolidity/syntaxTests/inlineAssembly/const_from_this.sol new file mode 100644 index 000000000..563d47851 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/const_from_this.sol @@ -0,0 +1,12 @@ +contract C { + bool constant c = this; + function f() public { + assembly { + let t := c + } + } +} +// ---- +// TypeError: (33-37): Type contract C is not implicitly convertible to expected type bool. +// TypeError: (33-37): Initial value for constant variable has to be compile-time constant. +// TypeError: (95-96): Only direct number constants and references to such constants are supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol index edae7549d..c43dbad6d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// DeclarationError: (52-85): Uninitialized storage pointer. +// TypeError: (95-96): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol index a0b6f71e7..68e3b2c44 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// DeclarationError: (84-95): Uninitialized storage pointer. +// TypeError: (105-106): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol new file mode 100644 index 000000000..b94334d38 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol @@ -0,0 +1,6 @@ +interface I { + function f() external m pure returns (uint); + modifier m() { _; } +} +// ---- +// SyntaxError: (16-60): Functions in interfaces cannot have modifiers. diff --git a/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol b/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol new file mode 100644 index 000000000..7197a4edb --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol @@ -0,0 +1,8 @@ +abstract contract C { + /// @param id Some identifier + /// @return value Some value + /// @return value2 Some value 2 + function vote(uint id) public virtual returns (uint value); +} +// ---- +// DocstringParsingError: (26-121): Documentation tag "@return value2 Some value 2" exceeds the number of return parameters. diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol index 4c1f96e6e..9035caa25 100644 --- a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol @@ -3,4 +3,3 @@ contract c { } // ---- // TypeError: (39-58): Type int_const 7 is not implicitly convertible to expected type contract c[10] storage pointer. -// DeclarationError: (60-83): Uninitialized storage pointer. diff --git a/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol b/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol new file mode 100644 index 000000000..50f0eb365 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol @@ -0,0 +1,7 @@ +contract C { + struct S {t t;} + function f(function(S memory) external) public {} +} +// ---- +// TypeError: (25-26): Name has to refer to a struct, enum or contract. +// TypeError: (53-61): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/structs/member_type_func.sol b/test/libsolidity/syntaxTests/structs/member_type_func.sol new file mode 100644 index 000000000..709a40e26 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/member_type_func.sol @@ -0,0 +1,8 @@ +contract C { + function f() public {} + struct S {f x;} + function g(function(S memory) external) public {} +} +// ---- +// TypeError: (50-51): Name has to refer to a struct, enum or contract. +// TypeError: (78-86): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol new file mode 100644 index 000000000..2fa48cfb1 --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol @@ -0,0 +1,12 @@ +contract C { + function f() { + (((((((((((,2),)),)),),))=4))); + } +} +// ---- +// SyntaxError: (15-69): No visibility specified. Did you intend to add "public"? +// TypeError: (46-47): Expression has to be an lvalue. +// TypeError: (60-61): Type int_const 4 is not implicitly convertible to expected type tuple(tuple(tuple(tuple(tuple(,int_const 2),),),),). +// TypeError: (37-61): Tuple component cannot be empty. +// TypeError: (36-62): Tuple component cannot be empty. +// TypeError: (35-63): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol new file mode 100644 index 000000000..2a5d62752 --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + int a; + (((a,),)) = ((1,2),3); + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol new file mode 100644 index 000000000..9028c7ccc --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol @@ -0,0 +1,8 @@ +contract B { + uint immutable x = 1; + function f() public pure returns (uint) { + return x; + } +} +// ---- +// TypeError: (100-101): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolutil/IpfsHash.cpp b/test/libsolutil/IpfsHash.cpp index 4514b2f4c..9c4fb5633 100644 --- a/test/libsolutil/IpfsHash.cpp +++ b/test/libsolutil/IpfsHash.cpp @@ -60,15 +60,50 @@ BOOST_AUTO_TEST_CASE(test_largest_unchunked) BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmbNDspMkzkMFKyS3eCJGedG7GWRQHSCzJCZLjxP7wyVAx"); } -// TODO This needs chunking implemented -//BOOST_AUTO_TEST_CASE(test_large) -//{ -// size_t length = 1310710; -// string data; -// data.resize(length, 0); -// BOOST_REQUIRE_EQUAL(data.size(), length); -// BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmNg7BJo8gEMDK8yGQbHEwPtycesnE6FUULX5iVd5TAL9f"); -//} +BOOST_AUTO_TEST_CASE(test_smallest_chunked) +{ + size_t length = 1024 * 256 + 1; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmbVuw4C4vcmVKqxoWtgDVobvcHrSn51qsmQmyxjk4sB2Q"); +} + +BOOST_AUTO_TEST_CASE(test_large) +{ + size_t length = 1310710; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmNg7BJo8gEMDK8yGQbHEwPtycesnE6FUULX5iVd5TAL9f"); +} + +BOOST_AUTO_TEST_CASE(test_largest_one_level) +{ + size_t length = 45613056; // 1024 * 256 * 174; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmY4HSz1oVGdUzb8poVYPLsoqBZjH6LZrtgnme9wWn2Qko"); +} + +BOOST_AUTO_TEST_CASE(test_smallest_multi_level) +{ + size_t length = 45613057; // 1024 * 256 * 174 + 1; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmehMASWcBsX7VcEQqs6rpR5AHoBfKyBVEgmkJHjpPg8jq"); +} + +BOOST_AUTO_TEST_CASE(test_multi_level_tree) +{ + size_t length = 46661632; + string data; + data.resize(length, 0); + BOOST_REQUIRE_EQUAL(data.size(), length); + BOOST_CHECK_EQUAL(ipfsHashBase58(data), "QmaTb1sT9hrSXJLmf8bxJ9NuwndiHuMLsgNLgkS2eXu3Xj"); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index ff1517eec..ee6d77dab 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -106,3 +107,51 @@ string yul::test::format(string const& _source, bool _yul) { return yul::AsmPrinter()(*parse(_source, _yul).first); } + +namespace +{ +std::map const validDialects = { + { + "evm", + [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& + { return yul::EVMDialect::strictAssemblyForEVMObjects(_evmVersion); } + }, + { + "evmTyped", + [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& + { return yul::EVMDialectTyped::instance(_evmVersion); } + }, + { + "yul", + [](langutil::EVMVersion) -> yul::Dialect const& + { return yul::Dialect::yulDeprecated(); } + }, + { + "ewasm", + [](langutil::EVMVersion) -> yul::Dialect const& + { return yul::WasmDialect::instance(); } + } +}; + +vector validDialectNames() +{ + vector names{size(validDialects), ""}; + transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); + + return names; +} +} + +yul::Dialect const& yul::test::dialect(std::string const& _name, langutil::EVMVersion _evmVersion) +{ + if (!validDialects.count(_name)) + BOOST_THROW_EXCEPTION(runtime_error{ + "Invalid Dialect \"" + + _name + + "\". Valid dialects are " + + util::joinHumanReadable(validDialectNames(), ", ", " and ") + + "." + }); + + return validDialects.at(_name)(_evmVersion); +} diff --git a/test/libyul/Common.h b/test/libyul/Common.h index 0b154e69c..b5f175ac2 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -23,6 +23,8 @@ #include +#include + #include #include #include @@ -53,4 +55,6 @@ parse(std::string const& _source, Dialect const& _dialect, langutil::ErrorList& Block disambiguate(std::string const& _source, bool _yul = true); std::string format(std::string const& _source, bool _yul = true); +solidity::yul::Dialect const& dialect(std::string const& _name, langutil::EVMVersion _evmVersion); + } diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index fdee56bda..a545b6124 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -48,17 +48,11 @@ using namespace solidity::frontend::test; using namespace std; -EwasmTranslationTest::EwasmTranslationTest(string const& _filename) +EwasmTranslationTest::EwasmTranslationTest(string const& _filename): + EVMVersionRestrictedTestCase(_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); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult EwasmTranslationTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 2d74d5501..db3a39df0 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -60,15 +60,11 @@ string toString(SideEffects const& _sideEffects) } } -FunctionSideEffects::FunctionSideEffects(string const& _filename) +FunctionSideEffects::FunctionSideEffects(string const& _filename): + TestCase(_filename) { - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open test input: \"" + _filename + "\".")); - file.exceptions(ios::badbit); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult FunctionSideEffects::run(ostream& _stream, string const& _linePrefix, bool _formatted) diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index f1b284042..9dd589748 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -38,23 +38,12 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -ObjectCompilerTest::ObjectCompilerTest(string const& _filename) +ObjectCompilerTest::ObjectCompilerTest(string const& _filename): + TestCase(_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); - - m_source = parseSourceAndSettings(file); - if (m_settings.count("optimize")) - { - m_optimize = true; - m_validatedSettings["optimize"] = "true"; - m_settings.erase("optimize"); - } - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_optimize = m_reader.boolSetting("optimize", false); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index 75e38c844..05accb695 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -19,61 +19,22 @@ #include #include -#include -#include -#include - #include #include +#include #include +#include + using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul::test; -namespace -{ -std::map const validDialects = { - { - "evm", - [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& - { return yul::EVMDialect::strictAssemblyForEVM(_evmVersion); } - }, - { - "evmTyped", - [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& - { return yul::EVMDialectTyped::instance(_evmVersion); } - }, - { - "yul", - [](langutil::EVMVersion) -> yul::Dialect const& - { return yul::Dialect::yulDeprecated(); } - }, - { - "ewasm", - [](langutil::EVMVersion) -> yul::Dialect const& - { return yul::WasmDialect::instance(); } - } -}; - -vector validDialectNames() -{ - vector names{size(validDialects), ""}; - transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); - - return names; -} -} - void SyntaxTest::parseAndAnalyze() { - string dialectName = m_validatedSettings.count("dialect") ? m_validatedSettings["dialect"] : "evmTyped"; - - yul::Dialect const& dialect = validDialects.at(dialectName)(m_evmVersion); - if (m_sources.size() != 1) BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."}); @@ -84,12 +45,12 @@ void SyntaxTest::parseAndAnalyze() ErrorReporter errorReporter{errorList}; auto scanner = make_shared(CharStream(source, name)); - auto parserResult = yul::Parser(errorReporter, dialect).parse(scanner, false); + auto parserResult = yul::Parser(errorReporter, *m_dialect).parse(scanner, false); if (parserResult) { yul::AsmAnalysisInfo analysisInfo; - yul::AsmAnalyzer(analysisInfo, errorReporter, dialect).analyze(*parserResult); + yul::AsmAnalyzer(analysisInfo, errorReporter, *m_dialect).analyze(*parserResult); } for (auto const& error: errorList) @@ -114,23 +75,9 @@ void SyntaxTest::parseAndAnalyze() } -void SyntaxTest::validateSettings() +SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): + CommonSyntaxTest(_filename, _evmVersion) { - CommonSyntaxTest::validateSettings(); - - if (!m_settings.count("dialect")) - return; - - string const dialect = m_settings["dialect"]; - m_validatedSettings["dialect"] = dialect; - m_settings.erase("dialect"); - - if (!validDialects.count(dialect)) - BOOST_THROW_EXCEPTION(runtime_error{ - "Invalid Dialect \"" + - dialect + - "\". Valid dialects are " + - joinHumanReadable(validDialectNames(), ", ", " and ") + - "." - }); + string dialectName = m_reader.stringSetting("dialect", "evmTyped"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); } diff --git a/test/libyul/SyntaxTest.h b/test/libyul/SyntaxTest.h index 087a6326c..b4c838b54 100644 --- a/test/libyul/SyntaxTest.h +++ b/test/libyul/SyntaxTest.h @@ -36,15 +36,13 @@ public: { return std::make_unique(_config.filename, _config.evmVersion); } - SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion): - CommonSyntaxTest(_filename, _evmVersion) {} + SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); virtual ~SyntaxTest() {} - - /// Validates the settings, i.e. moves them from m_settings to m_validatedSettings. - /// Throws a runtime exception if any setting is left at this class (i.e. unknown setting). - void validateSettings() override; protected: void parseAndAnalyze() override; + +private: + Dialect const* m_dialect = nullptr; }; } diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index f37d18383..937ffb57e 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -45,17 +45,11 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -YulInterpreterTest::YulInterpreterTest(string const& _filename) +YulInterpreterTest::YulInterpreterTest(string const& _filename): + EVMVersionRestrictedTestCase(_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); - - m_source = parseSourceAndSettings(file); - m_expectation = parseSimpleExpectations(file); + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index e488460fc..9458174ed 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -89,7 +89,8 @@ using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -YulOptimizerTest::YulOptimizerTest(string const& _filename) +YulOptimizerTest::YulOptimizerTest(string const& _filename): + EVMVersionRestrictedTestCase(_filename) { boost::filesystem::path path(_filename); @@ -97,38 +98,12 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename) BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); m_optimizerStep = std::prev(std::prev(path.end()))->string(); - ifstream file(_filename); - soltestAssert(file, "Cannot open test contract: \"" + _filename + "\"."); - file.exceptions(ios::badbit); + m_source = m_reader.source(); - m_source = parseSourceAndSettings(file); - if (m_settings.count("dialect")) - { - auto dialectName = m_settings["dialect"]; - if (dialectName == "yul") - m_dialect = &Dialect::yulDeprecated(); - else if (dialectName == "ewasm") - m_dialect = &WasmDialect::instance(); - else if (dialectName == "evm") - m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); - else if (dialectName == "evmTyped") - m_dialect = &EVMDialectTyped::instance(solidity::test::CommonOptions::get().evmVersion()); - else - BOOST_THROW_EXCEPTION(runtime_error("Invalid dialect " + dialectName)); + auto dialectName = m_reader.stringSetting("dialect", "evm"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); - m_validatedSettings["dialect"] = dialectName; - m_settings.erase("dialect"); - } - else - m_dialect = &EVMDialect::strictAssemblyForEVMObjects(solidity::test::CommonOptions::get().evmVersion()); - - if (m_settings.count("step")) - { - m_validatedSettings["step"] = m_settings["step"]; - m_settings.erase("step"); - } - - m_expectation = parseSimpleExpectations(file); + m_expectation = m_reader.simpleExpectations(); } TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) @@ -375,21 +350,8 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line return TestResult::FatalError; } - m_obtainedResult = AsmPrinter{*m_dialect}(*m_ast) + "\n"; + m_obtainedResult = "step: " + m_optimizerStep + "\n\n" + AsmPrinter{ *m_dialect }(*m_ast) + "\n"; - if (m_optimizerStep != m_validatedSettings["step"]) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << - _linePrefix << - "Invalid optimizer step. Given: \"" << - m_validatedSettings["step"] << - "\", should be: \"" << - m_optimizerStep << - "\"." << - endl; - return TestResult::FatalError; - } if (m_expectation != m_obtainedResult) { string nextIndentLevel = _linePrefix + " "; @@ -408,12 +370,6 @@ void YulOptimizerTest::printSource(ostream& _stream, string const& _linePrefix, printIndented(_stream, m_source, _linePrefix); } -void YulOptimizerTest::printUpdatedSettings(ostream& _stream, const string& _linePrefix, const bool _formatted) -{ - m_validatedSettings["step"] = m_optimizerStep; - EVMVersionRestrictedTestCase::printUpdatedSettings(_stream, _linePrefix, _formatted); -} - void YulOptimizerTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const { printIndented(_stream, m_obtainedResult, _linePrefix); diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 41e38fca0..99e239e3b 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -57,7 +57,6 @@ public: TestResult 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 printUpdatedSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; private: diff --git a/test/libyul/yulOptimizerTests/blockFlattener/basic.yul b/test/libyul/yulOptimizerTests/blockFlattener/basic.yul index 8558737cf..e57617436 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/basic.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/basic.yul @@ -8,9 +8,9 @@ } let z := mload(2) } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // let _1 := mload(0) // let f_a := mload(1) diff --git a/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul index dfb88a178..a5be68a7c 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul @@ -3,9 +3,9 @@ a := add(a, 1) } } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } // { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul index ca5055ac9..f31c5f979 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul @@ -8,9 +8,9 @@ } let t := add(3, 9) } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // if add(mload(7), sload(mload(3))) // { diff --git a/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul b/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul index a397000e4..d7fd4b45a 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul @@ -14,9 +14,9 @@ a := add(a, c) } } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // let a := 3 // let b := 4 diff --git a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul index f3243296b..0b2a8245d 100644 --- a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul +++ b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul @@ -5,9 +5,9 @@ default { a := 3 { a := 4 } } a := 5 } -// ==== -// step: blockFlattener // ---- +// step: blockFlattener +// // { // let a := 1 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul index 07eca2709..739ba78bf 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/called_from_non_function.yul @@ -5,9 +5,9 @@ function h() -> z { z := g() } a := h() } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { // let a // a := h() diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul index 0630fe35f..ac04f96a8 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_different_names.yul @@ -8,7 +8,7 @@ function d() -> w { w := c() } } } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul index 9873c717f..02ac2317d 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/nested_same_name.yul @@ -8,7 +8,7 @@ function y() -> x { x := z() } } } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul b/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul index c0fe6834a..4398bc29f 100644 --- a/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul +++ b/test/libyul/yulOptimizerTests/circularReferencesPruner/trivial.yul @@ -2,7 +2,7 @@ function f() -> x { x := g() } function g() -> x { x := f() } } -// ==== -// step: circularReferencesPruner // ---- +// step: circularReferencesPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul index a077efcf1..ac4ed1b05 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul @@ -6,9 +6,9 @@ } mstore(1, codesize()) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := 1 // let b := codesize() diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul index e664aa781..8de646ae3 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul @@ -3,9 +3,9 @@ if b { b := 1 } let c := 1 } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let b := 1 // if b { b := b } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul index ab08c0dee..3a51b6626 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul @@ -23,9 +23,9 @@ p_1 := add(array, _22) } } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let _13 := 0x20 // let _14 := allocate(_13) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul index 89075e3bb..d0e124f6b 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/clear_not_needed.yul @@ -7,9 +7,9 @@ a := 9 sstore(x, 3) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := calldataload(0) // let x := calldataload(0x20) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul index 957232a8f..1fd8f84f1 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul @@ -23,9 +23,9 @@ let _11 := array_index_access(x, _10) mstore(_11, _9) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // function allocate(size) -> p // { diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul index 4725a444e..2848ef3f1 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul @@ -35,9 +35,9 @@ } sstore(_1, sum_50) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let _1 := 0 // let _33 := calldataload(_1) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul index 3b3e65dbd..ac0fa0f52 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/movable_functions.yul @@ -7,9 +7,9 @@ let c := double_with_se(i) let d := double_with_se(i) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // function double(x) -> y // { y := add(x, x) } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul index 42b4274ce..a9f5664ec 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul @@ -2,9 +2,9 @@ let a := mload(1) let b := mload(1) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := mload(1) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul index 6fee35665..ed8916e63 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul @@ -2,9 +2,9 @@ let a := gas() let b := gas() } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := gas() // let b := gas() diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul index 4c1e0feeb..0f723c6c1 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul @@ -14,9 +14,9 @@ object "main" { } data "abc" "Hello, World!" } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let r := "abc" // let a := datasize("abc") diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul index 44d08679f..5e5b79696 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul @@ -10,9 +10,9 @@ mstore(0, calldataload(0)) mstore(0, x) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := 10 // let x := 20 diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul index 2457b3b56..ab0d0a1ea 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul index 1fdc65d43..c64fc93f9 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul @@ -2,9 +2,9 @@ let a := mul(1, codesize()) let b := mul(1, codesize()) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := mul(1, codesize()) // let b := a diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul index 178b8c523..46bf80eb7 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_return.yul @@ -9,9 +9,9 @@ let b := 0 sstore(a, b) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // function f() -> x // { diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul index 0012af037..ab14e0425 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/unassigned_variables.yul @@ -5,9 +5,9 @@ let b mstore(sub(a, b), 7) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul index 9f1bda665..52198a173 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul @@ -12,9 +12,9 @@ a := b mstore(2, a) } -// ==== -// step: commonSubexpressionEliminator // ---- +// step: commonSubexpressionEliminator +// // { // let a := mload(0) // let b := add(a, 7) diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul index f0b46072f..c7cc17fc8 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type.yul @@ -6,8 +6,9 @@ } // ==== // dialect: yul -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y:bool := false // for { } true { } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul index ab523246e..de92aad7a 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/add_correct_type_wasm.yul @@ -6,8 +6,9 @@ } // ==== // dialect: ewasm -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y:i32 := 0:i32 // for { } true { } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul index 00c46fcdf..d49ec0709 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_break.yul @@ -4,9 +4,9 @@ if y { break } } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul index 1e6b0828a..9246c162b 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_after_if_continue.yul @@ -4,9 +4,9 @@ if y { continue } } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul index e936287db..35ba33c6e 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_condition.yul @@ -7,9 +7,9 @@ x := 2 } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // let y := mload(0) diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul index c541590bd..4207525c7 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/clear_before_for_post.yul @@ -8,9 +8,9 @@ } sstore(0, x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x // for { } x { sstore(1, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul index 101407783..6aa3ba57d 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_if_break_is_not_last.yul @@ -7,9 +7,9 @@ sstore(10, x) } } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // for { } 1 { } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul index 86983b316..ef262b2e6 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/no_opt_inside_if.yul @@ -3,9 +3,9 @@ if x { sstore(0, x) } sstore(1, x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // if x { sstore(0, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul index ca8bbba87..0326a5c3a 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_after_terminating_if.yul @@ -3,9 +3,9 @@ if x { sstore(0, x) revert(0, 0) } sstore(1, x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := mload(0) // if x diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul index 1f4da9eea..58a43c0d2 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/opt_switch.yul @@ -7,9 +7,9 @@ pop(x) } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { // let x := calldataload(0) // switch x diff --git a/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul index f37bf3d01..17e4d03a2 100644 --- a/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul +++ b/test/libyul/yulOptimizerTests/conditionalSimplifier/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: conditionalSimplifier // ---- +// step: conditionalSimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul index 01b301610..a12f171c6 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_break.yul @@ -5,9 +5,9 @@ y := 0 } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul index d4014c2b0..86f99e298 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_after_if_continue.yul @@ -5,9 +5,9 @@ y := 0 } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let y := mload(0x20) // for { } and(y, 8) { pop(y) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul index de41539f0..2ea623e7a 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_condition.yul @@ -9,9 +9,9 @@ x := 2 } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // let y := mload(0) diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul index d84611352..a7fadc434 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/clear_before_for_post.yul @@ -7,9 +7,9 @@ } sstore(0, x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x // for { } x { sstore(1, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul index a697c6d25..e5342b2a6 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_if_break_is_not_last.yul @@ -9,9 +9,9 @@ sstore(10, x) } } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // for { } 1 { } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul index 830ae064c..8c94644ce 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/no_opt_inside_if.yul @@ -4,9 +4,9 @@ x := 0 sstore(1, x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // if x { sstore(0, x) } diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul index 13ffc0bac..d90e1fc4b 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_after_terminating_if.yul @@ -4,9 +4,9 @@ x := 0 sstore(1, x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := mload(0) // if x diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul index 107b2923b..361b0f77b 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/opt_switch.yul @@ -8,9 +8,9 @@ pop(x) } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { // let x := calldataload(0) // switch x diff --git a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul index cb88da72b..176d7fd57 100644 --- a/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul +++ b/test/libyul/yulOptimizerTests/conditionalUnsimplifier/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: conditionalUnsimplifier // ---- +// step: conditionalUnsimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul b/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul index 47b76fa7c..a7c4126ac 100644 --- a/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul +++ b/test/libyul/yulOptimizerTests/constantOptimiser/difficult.yul @@ -4,9 +4,9 @@ let z := 0xffff0000ffff0000ffff0000ffff0000ff00ff00ffff0000ffff0000ffff0000 let w := 0xffffffff000000ffffef000001feff000067ffefff0000ff230002ffee00fff7 } -// ==== -// step: constantOptimiser // ---- +// step: constantOptimiser +// // { // let x := 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00 // let y := 0x1100ff00ff00ff001100ff00ff001100ff00ff001100ff00ff001100ff001100 diff --git a/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul b/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul index 6371638eb..82dccdab4 100644 --- a/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul +++ b/test/libyul/yulOptimizerTests/constantOptimiser/gaps.yul @@ -7,8 +7,9 @@ } // ==== // EVMVersion: >=constantinople -// step: constantOptimiser // ---- +// step: constantOptimiser +// // { // let a := shl(172, 1) // let x := add(shl(248, 17), 0xffffffffffffffffffffffff23) diff --git a/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul b/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul index 0315fdab8..af8a08886 100644 --- a/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul +++ b/test/libyul/yulOptimizerTests/constantOptimiser/smallNumbers.yul @@ -4,9 +4,9 @@ for { let i := 0xff00 } lt(i, 2) { i := add(i, 3) } { } } -// ==== -// step: constantOptimiser // ---- +// step: constantOptimiser +// // { // let x := 8 // let y := 0xffff diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul index 87450d939..bf74a266c 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_movable_condition.yul @@ -1,7 +1,7 @@ { let a := mload(0) if a {} } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let a := mload(0) // pop(a) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul index 737ff7bf1..a204e6cec 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/empty_if_non_movable_condition.yul @@ -1,5 +1,5 @@ { if mload(0) {} } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { pop(mload(0)) } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul index af7726df9..f0bf4b7c2 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/remove_leave.yul @@ -3,9 +3,9 @@ function g() -> x { leave x := 7 } function h() -> x { if x { leave } } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // function f() -> x // { x := 7 } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul index 16e718d2e..f52551ab4 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_only_default.yul @@ -1,9 +1,9 @@ { switch mload(0) default { mstore(1, 2) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // pop(mload(0)) // { mstore(1, 2) } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul index fb2434ec7..a04b87ae6 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_all.yul @@ -10,9 +10,9 @@ case 1 { } default { } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // pop(add(y, 4)) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul index 4b4f2cc0c..03879b263 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_case.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } case 2 { y := 10 } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul index 57beacaa1..223760723 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_cases.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } default { } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // if eq(1, calldataload(0)) { y := 9 } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul index c5c4e44a7..7c6283a8f 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_remove_empty_default_case.yul @@ -5,9 +5,9 @@ case 2 { y := 10 } default { } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // let y := 200 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul index 4a3558b65..b314cd96d 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/switch_to_if.yul @@ -1,9 +1,9 @@ { switch calldataload(0) case 2 { mstore(0, 0) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if eq(2, calldataload(0)) { mstore(0, 0) } // } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul index 6f7390d53..8e48e2b65 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for.yul @@ -4,9 +4,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if calldatasize() { mstore(4, 5) } // } diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul index 2b6397269..7bf16956e 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested.yul @@ -8,9 +8,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // for { } calldatasize() { mstore(8, 9) } // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul index 5412a7952..806a8ebcb 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_nested_reversed.yul @@ -7,9 +7,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if calldatasize() // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul index d6f7e5d18..b8bf66163 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert.yul @@ -5,9 +5,9 @@ revert(0, x) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // if calldatasize() // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul index 7ef55bc3c..e8d034733 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_revert_plus_break.yul @@ -6,9 +6,9 @@ revert(0, x) } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // for { } calldatasize() { mstore(1, 2) } // { diff --git a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul index 9d5ce5b84..b92efd885 100644 --- a/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul +++ b/test/libyul/yulOptimizerTests/controlFlowSimplifier/terminating_for_with_continue.yul @@ -5,9 +5,9 @@ break } } -// ==== -// step: controlFlowSimplifier // ---- +// step: controlFlowSimplifier +// // { // for { } calldatasize() { mstore(1, 2) } // { diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul index 3d350c43b..ef17a9708 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/conditional_break.yul @@ -13,9 +13,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul index a0d751938..c7a3d98e1 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_break.yul @@ -14,9 +14,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul index 61eae9ff2..d6b9f9d23 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_continue.yul @@ -14,9 +14,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul index 90dae56f0..ce3c00672 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_leave.yul @@ -18,9 +18,9 @@ pop(f()) } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // function f() -> x // { diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul index be13641c2..7e37d6d4b 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_revert.yul @@ -15,9 +15,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let b := 20 // revert(0, 0) diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul index ee4227b6a..ad1575736 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/early_stop.yul @@ -15,9 +15,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let b := 20 // stop() diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul index 295a49504..f5e67f77f 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/for_loop_init_decl.yul @@ -4,7 +4,7 @@ let i_1 := i_0 } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { stop() } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul index e1d0f30fe..d5df28fb0 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/function_after_revert.yul @@ -12,9 +12,9 @@ pop(add(1, 1)) } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // fun() // revert(0, 0) diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul index b89c1e0b7..088394ee0 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/nested_revert.yul @@ -12,9 +12,9 @@ y := 10 } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let y := mload(0) // switch y diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul index c1003538d..0e0a2b9c4 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/no_removal.yul @@ -4,9 +4,9 @@ } mstore(0, 0) } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // { revert(0, 0) } // mstore(0, 0) diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul index 77e4303b6..28425b6e1 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_break.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul index e3a0b7a2f..e01ae0aed 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_continue.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let a := 20 // for { } lt(a, 40) { a := add(a, 2) } diff --git a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul index 561ac2066..6999c39c0 100644 --- a/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul +++ b/test/libyul/yulOptimizerTests/deadCodeEliminator/normal_stop.yul @@ -15,9 +15,9 @@ stop() } -// ==== -// step: deadCodeEliminator // ---- +// step: deadCodeEliminator +// // { // let b := 20 // let a := 20 diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul index c6b246ad0..30f2e5479 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul index 7a4cfe739..f99c4e447 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul @@ -8,8 +8,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b, c, d, f } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul index def0a2d31..6b6da1f18 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b, c } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul index b403dc095..d55afc482 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul @@ -1,8 +1,9 @@ { { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { // let aanteuhdaoneudbrgkjiuaothduiathudaoeuh diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul index bfe22582a..4ea58a6ef 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/smoke.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: disambiguator // ---- +// step: disambiguator +// // { } diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul index 2ed2b9ad1..676416437 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul @@ -1,6 +1,7 @@ { } // ==== -// step: disambiguator // dialect: yul // ---- +// step: disambiguator +// // { } diff --git a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul index 0948b51b9..16ee5353f 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a, b, c } // { diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables.yul b/test/libyul/yulOptimizerTests/disambiguator/variables.yul index 7b197f420..65c93bbb9 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables.yul @@ -1,8 +1,9 @@ { { let a:u256 } { let a:u256 } } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { let a } // { let a_1 } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul index 9c72b82ff..e2cf23756 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul @@ -1,8 +1,9 @@ { { let a:u256 let a_1:u256 } { let a:u256 } } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { // let a diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul index fecb67a65..a78af7dab 100644 --- a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: disambiguator // ---- +// step: disambiguator +// // { // { // let c diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul index e400e69ae..5f561185d 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/multiple_complex.yul @@ -54,9 +54,9 @@ } } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // pop(f(1, 2, 3)) // pop(f(4, 5, 6)) diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul index 43629175b..69045f364 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple.yul @@ -4,9 +4,9 @@ function f() { mstore(1, mload(0)) } function g() { mstore(1, mload(0)) } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // f() // f() diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul index 25ffa4017..5967adc0e 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/simple_different_vars.yul @@ -4,9 +4,9 @@ function f() -> b { let a := mload(0) b := a } function g() -> a { let b := mload(0) a := b } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // pop(f()) // pop(f()) diff --git a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul index 9de10889b..490bfd072 100644 --- a/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul +++ b/test/libyul/yulOptimizerTests/equivalentFunctionCombiner/switch_case_order.yul @@ -4,9 +4,9 @@ function f(x) { switch x case 0 { mstore(0, 42) } case 1 { mstore(1, 42) } } function g(x) { switch x case 1 { mstore(1, 42) } case 0 { mstore(0, 42) } } } -// ==== -// step: equivalentFunctionCombiner // ---- +// step: equivalentFunctionCombiner +// // { // f(0) // f(1) diff --git a/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul b/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul index 37649a572..12228009a 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/argument_duplication_heuristic.yul @@ -16,9 +16,9 @@ let y11:= ref1(y1) let y12:= ref3(y1) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function ref1(a) -> x // { x := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul index e95c4144c..49237c2f6 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul @@ -2,9 +2,9 @@ function f(a) -> x { x := add(a, a) } let y := f(calldatasize()) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := add(a, a) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul index d19082f2e..42dc4f015 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul @@ -3,9 +3,9 @@ function g(b, c) -> y { y := mul(mload(c), f(b)) } let y := g(calldatasize(), 7) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := add(a, a) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul index 923032af0..337751a72 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul @@ -3,9 +3,9 @@ function g(b, s) -> y { y := f(b, f(s, s)) } let y := g(calldatasize(), 7) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a, r) -> x // { x := g(a, f(r, f(r, r))) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul index 1b2b702fe..1698ac5ac 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul @@ -3,9 +3,9 @@ function f(a) -> x { x := a } let y := f(mload(2)) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := a } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul index 5a116bcc3..48acca95c 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul @@ -6,9 +6,9 @@ function h() -> z { mstore(0, 4) z := mload(0) } let r := f(g(), h()) } -// ==== -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a, b) -> x // { x := add(b, a) } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul index 8c3cd28e3..2b6dba240 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f() -> x // { x := 2 } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul index 5b73ed3fe..0456654e3 100644 --- a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul +++ b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: expressionInliner // ---- +// step: expressionInliner +// // { // function f(a) -> x // { x := a } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul index b2a204482..045168cf1 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul @@ -10,9 +10,9 @@ let z := 3 let t := add(z, 9) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // if add(mload(7), sload(mload(3))) { let y := add(mload(3), 3) } // let t := add(3, 9) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul index 07982ca47..461285ae2 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul @@ -4,9 +4,9 @@ let x := mul(add(b, a), mload(2)) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(3) // let b := mload(6) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul index 9d9a139a2..5fcd2c5bf 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul @@ -4,9 +4,9 @@ let x := mul(add(b, a), 2) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // sstore(mul(add(mload(6), mload(2)), 2), 3) // } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul index 95a0eccb3..ed209e597 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul @@ -3,9 +3,9 @@ let a := mload(2) let b := add(a, a) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // let b := add(a, a) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul index d8624dc14..2837f7033 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul @@ -7,9 +7,9 @@ let x := mul(a, add(2, b)) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // sstore(mul(a, add(2, mload(6))), 3) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul index 6296c3778..d69f9d8a5 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul @@ -4,9 +4,9 @@ let x := mul(add(a, b), 2) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // sstore(mul(add(a, mload(6)), 2), 3) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul index 02c4466a2..0e4f1916a 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul @@ -11,9 +11,9 @@ } sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let x := calldataload(mload(2)) // sstore(x, 3) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul index 0cc4e8f6d..b453446ad 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul @@ -1,9 +1,9 @@ { for { let b := mload(1) } b {} {} } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // for { let b := mload(1) } b { } // { } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul index 144476904..ab41999d5 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul @@ -2,9 +2,9 @@ let a := mload(0) for { } a {} {} } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(0) // for { } a { } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul index 12fa6ec51..30bf46634 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul @@ -5,9 +5,9 @@ x := add(a, 3) } } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // function f(a) -> x // { diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul index 97661414f..e327d0b33 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul @@ -4,9 +4,9 @@ let b := mload(a) a := 4 } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let a := mload(2) // let b := mload(a) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul index 03d2a7aee..5ec8a900a 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul @@ -3,9 +3,9 @@ let x := calldataload(a) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // sstore(calldataload(mload(2)), 3) // } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul index 21bf67c89..812cb7e7c 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul @@ -5,9 +5,9 @@ let d := add(b, c) sstore(d, 0) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // let b := sload(mload(3)) // sstore(add(b, mload(7)), 0) diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul index 7eb5f5285..77f73f112 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul index e8e787762..08f7bb9fb 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul @@ -14,9 +14,9 @@ let z := 3 let t := add(z, 9) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // switch add(mload(7), sload(mload(3))) // case 3 { let y := add(mload(3), 3) } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul index cf2b4a615..d6c652e2c 100644 --- a/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul +++ b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul @@ -5,9 +5,9 @@ let x := mul(add(c, b), a) sstore(x, 3) } -// ==== -// step: expressionJoiner // ---- +// step: expressionJoiner +// // { // sstore(mul(add(mload(7), mload(6)), mload(2)), 3) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul index 809f6ff23..783319395 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul @@ -3,9 +3,9 @@ let c, d := f() let y := add(d, add(c, 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> x, z // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul index 94caf0782..d465600e3 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul @@ -5,8 +5,9 @@ } // ==== // EVMVersion: >byzantium -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := calldataload(0) // let a := shr(248, x) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul index 36c56834b..6649a86b5 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul @@ -10,8 +10,9 @@ } // ==== // EVMVersion: >byzantium -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := calldataload(0) // let a := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul index f506c41bd..4211b6afe 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul @@ -8,8 +8,9 @@ } // ==== // EVMVersion: >byzantium -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := calldataload(0) // let a := and(shl(8, x), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul index 983a3b251..2f655efba 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul @@ -1,5 +1,5 @@ { let a := add(7, sub(mload(0), 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := mload(0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul index efbcf8712..8bf04b987 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul @@ -1,5 +1,5 @@ { let a := add(1, mul(3, 4)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := 13 } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul index cd74d73ad..bbda84653 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul @@ -3,9 +3,10 @@ let b := and(0xffffffffffffffffffffffffffffffffffffffff, create2(0, 0, 0x20, 0)) } // ==== -// step: expressionSimplifier // EVMVersion: >=constantinople // ---- +// step: expressionSimplifier +// // { // let a := create2(0, 0, 0x20, 0) // let b := create2(0, 0, 0x20, 0) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul index c7cc887dc..44a8cabb0 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul @@ -2,9 +2,9 @@ let a := and(create(0, 0, 0x20), 0xffffffffffffffffffffffffffffffffffffffff) let b := and(0xffffffffffffffffffffffffffffffffffffffff, create(0, 0, 0x20)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := create(0, 0, 0x20) // let b := create(0, 0, 0x20) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul index e7b60d2df..38502d22c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul @@ -1,5 +1,5 @@ { let a := sub(calldataload(0), calldataload(0)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := 0 } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul index 2838eb193..5f3d268ca 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul @@ -1,7 +1,7 @@ { let a := sub(calldataload(1), calldataload(0)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := sub(calldataload(1), calldataload(0)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul index 554d080b2..d75ceb831 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul @@ -2,9 +2,9 @@ let a := mload(0) let b := sub(a, a) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := mload(0) // let b := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul index 0c92afe38..73b6bfa54 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul @@ -2,9 +2,9 @@ function f() -> a {} let b := add(7, sub(f(), 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> a // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul index 83906d675..b8b3ee35d 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul @@ -2,9 +2,9 @@ let a := 10 for { } iszero(eq(a, 0)) { a := add(a, 1) } {} } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := 10 // for { } iszero(iszero(a)) { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul index 9ad889243..fc75f131f 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul @@ -2,9 +2,9 @@ let a := mload(sub(7, 7)) let b := sub(a, 0) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := mload(0) // let b := a diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul index d5e7c0ad4..073db8d4e 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul @@ -5,9 +5,9 @@ // create cannot be removed. let d := byte(33, create(0, 0, 0x20)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := calldataload(0) // let b := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul index 6d7b03035..65a325d6c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 8))) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // mstore(0, and(calldataload(0), 255)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul index d949b2f17..d4e35af33 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 255))) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul index 1430fd718..b7bc3e4f9 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul @@ -2,9 +2,9 @@ function f(a) -> b { } let c := sub(f(0), f(1)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f(a) -> b // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul index cc819fff8..b43440128 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul @@ -3,9 +3,9 @@ function f2() -> b { } let c := sub(f1(), f2()) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f1() -> a // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul index fc43fa788..c0fa43e2c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul @@ -3,9 +3,9 @@ function f() -> a { } let b := sub(f(), f()) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> a // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul index cee563173..d153ae724 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul @@ -3,9 +3,9 @@ { let a := div(keccak256(0, 0), 0) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := div(keccak256(0, 0), 0) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul index 8178c6d03..fbc5befd0 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul @@ -3,9 +3,9 @@ x := 0 mstore(0, add(7, x)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let x := mload(0) // x := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul index cb5303eaf..b81ae9619 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul @@ -6,8 +6,9 @@ } // ==== // EVMVersion: >=constantinople -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := shr(248, calldataload(0)) // let b := shr(248, calldataload(0)) diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul index 8d69bcc8d..550c7becb 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul @@ -6,8 +6,9 @@ } // ==== // EVMVersion: >=constantinople -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let a := 0 // let b := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul index 8586396c4..c1c0093ef 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul @@ -4,9 +4,9 @@ let y := add(d, add(c, 7)) } } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // function f() -> c, d // { let y := 7 } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul index 1d398f833..9ca956c10 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul @@ -1,7 +1,7 @@ { let a := add(0, mload(0)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { let a := mload(0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul index 7e181a097..14d65c4e9 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/side_effects_in_for_condition.yul @@ -4,9 +4,10 @@ } } // ==== -// step: expressionSimplifier // EVMVersion: >byzantium // ---- +// step: expressionSimplifier +// // { // for { } div(create(0, 1, 0), shl(msize(), 1)) { } // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul index 7562dfb47..30a2f5e90 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul index e5db5ff76..e88877365 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul @@ -3,9 +3,9 @@ let c, d let y := add(d, add(c, 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: 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 index f8480a327..d66d4efb2 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul @@ -4,9 +4,9 @@ let d let y := add(d, add(c, 7)) } -// ==== -// step: expressionSimplifier // ---- +// step: expressionSimplifier +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul index 2eb72c112..d56a5285e 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul @@ -7,9 +7,9 @@ } } } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let _1 := 0 // let x := calldataload(_1) diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul index bb73118ec..294cac14a 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul @@ -5,9 +5,9 @@ } sstore(x, f(mload(2), mload(2))) } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let _1 := 3 // let _2 := 7 diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul index 969991871..60492e027 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul @@ -9,9 +9,9 @@ object "main" { } data "abc" "Hello, World!" } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let x := dataoffset("abc") // let y := datasize("abc") diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul index c47eebe7b..c5f979bf1 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul index 3608cab4f..3664a7e26 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul @@ -5,9 +5,9 @@ default { mstore(0, mload(3)) } x := add(mload(3), 4) } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let x := 8 // let _1 := 0 diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul index 3d48eed79..a70827b93 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul @@ -1,9 +1,9 @@ { mstore(add(calldataload(2), mload(3)), 8) } -// ==== -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // let _1 := 8 // let _2 := 3 diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul b/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul index 37f40f5e6..4bae497d6 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/typed.yul @@ -11,8 +11,9 @@ } // ==== // dialect: ewasm -// step: expressionSplitter // ---- +// step: expressionSplitter +// // { // function fun(x:i32, y) -> t:i32, z:i32 // { diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul index 95e171475..031507a38 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/cond_types.yul @@ -6,9 +6,9 @@ for { } a { } { } for { } add(a, a) { } { } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // let a := 1 // for { } 42 { } diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul index 6f1ad5458..41c9e4234 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/empty_body.yul @@ -1,9 +1,9 @@ { for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // for { let a := 1 } true { a := add(a, 1) } // { diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul index 292284a65..acbe3e456 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/nested.yul @@ -13,9 +13,9 @@ mstore(b,b) } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // let random := 42 // for { diff --git a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul index f47026462..bbe782307 100644 --- a/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul +++ b/test/libyul/yulOptimizerTests/forLoopConditionIntoBody/simple.yul @@ -4,9 +4,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopConditionIntoBody // ---- +// step: forLoopConditionIntoBody +// // { // let random := 42 // for { let a := 1 } true { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul index 951a5fbe6..6304c4de5 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/complex_pre.yul @@ -5,9 +5,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let random := 42 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul index 9fa5fd37e..38a00f752 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/empty_pre.yul @@ -4,9 +4,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let a := 1 // for { } iszero(eq(a, 10)) { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul index c751f4483..331d9cdde 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/nested.yul @@ -13,9 +13,9 @@ mstore(b,b) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let random := 42 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul b/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul index c8db1e5aa..0dad89961 100644 --- a/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul +++ b/test/libyul/yulOptimizerTests/forLoopInitRewriter/simple.yul @@ -4,9 +4,9 @@ a := add(a, 1) } } -// ==== -// step: forLoopInitRewriter // ---- +// step: forLoopInitRewriter +// // { // let random := 42 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul index b3ec1bbe8..259e4aeda 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul @@ -4,9 +4,9 @@ let b3, c3 := f(a1) let b4, c4 := f(c3) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_2 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul index fd9d893ae..39c3bb295 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul @@ -8,9 +8,9 @@ r := add(a, calldatasize()) } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _2 := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul index a088e1681..151b7596c 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul @@ -15,9 +15,9 @@ // This should be inlined because it is a constant as well (zero) let s := f(a3) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_1 := mload(2) diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul index b8c05b72c..4bcfe6f19 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul @@ -10,9 +10,9 @@ // Single-use functions are always inlined. let r := f(mload(1)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_6 := mload(1) diff --git a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul index 8e9c2019d..b2162c520 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul @@ -7,9 +7,9 @@ mstore(0, verylongfunctionname(verylongvariablename2)) mstore(1, verylongvariablename2) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let verylongvariablename2_1 := 3 diff --git a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul index f40563602..7bc959ec8 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul @@ -5,9 +5,9 @@ } let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5))) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _2 := mload(5) diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul index 2f8a85842..56cac2704 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul @@ -3,9 +3,9 @@ function g(b, c) -> y { y := mul(mload(c), f(b)) } let y := g(f(3), 7) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _1 := 7 diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index 46980522d..98c8e3010 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -22,9 +22,9 @@ f(100) } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let x_8 := 100 diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul index 7d90e0118..21369f10b 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul @@ -6,9 +6,9 @@ let r, s := f(mload(0)) mstore(r, s) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul index f9f01a193..143ae35b2 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return_typed.yul @@ -6,8 +6,9 @@ } // ==== // dialect: evmTyped -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := mload(3) diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul index 4a7837ca2..c1ce89bcf 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_function.yul @@ -9,9 +9,9 @@ x := f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(f(2))))))))))))))))))) } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // function f(a) -> b // { b := sload(mload(a)) } 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 index 337612d37..10ccc2a37 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_into_big_global_context.yul @@ -7,9 +7,9 @@ // 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))))))))))))))))))) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_20 := 2 diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul b/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul index 552ce9a82..a22025c4b 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_inline_leave.yul @@ -4,9 +4,9 @@ let a1 := calldataload(0) f(a1) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_2 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_return.yul b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul index c45e7a3c7..2897b5151 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/no_return.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul @@ -4,9 +4,9 @@ } f(mload(0)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul index 94282bf5c..5678a13fd 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul @@ -9,9 +9,9 @@ r := a } } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let a_3 := 0 diff --git a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul index 373416925..c46505c79 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul @@ -8,9 +8,9 @@ } pop(add(f(7), 2)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _1 := 2 diff --git a/test/libyul/yulOptimizerTests/fullInliner/recursion.yul b/test/libyul/yulOptimizerTests/fullInliner/recursion.yul index 82632a9e7..375327e65 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/recursion.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/recursion.yul @@ -4,9 +4,9 @@ } f(mload(0)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { f(mload(0)) } // function f(a) diff --git a/test/libyul/yulOptimizerTests/fullInliner/simple.yul b/test/libyul/yulOptimizerTests/fullInliner/simple.yul index af6fa8283..2c9d979b5 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/simple.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/simple.yul @@ -5,9 +5,9 @@ } let y := add(f(sload(mload(2))), mload(7)) } -// ==== -// step: fullInliner // ---- +// step: fullInliner +// // { // { // let _2 := mload(7) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul b/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul index 4c7fa2692..968ec4767 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul @@ -2,9 +2,9 @@ let a := add(7, sub(mload(0), 7)) mstore(a, 0) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _2 := 0 // mstore(mload(_2), _2) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/constants.yul b/test/libyul/yulOptimizerTests/fullSimplify/constants.yul index 2ea5ea442..0b31d361f 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/constants.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/constants.yul @@ -2,7 +2,7 @@ let a := add(1, mul(3, 4)) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { mstore(0, 13) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul index df7712b7a..452ceaae6 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul @@ -2,7 +2,7 @@ let a := sub(calldataload(0), calldataload(0)) mstore(a, 0) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul index bc7bf5b5a..8c5e4e5df 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul @@ -2,9 +2,9 @@ let a := sub(calldataload(1), calldataload(0)) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _1 := 0 // mstore(_1, sub(calldataload(1), calldataload(_1))) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul index 1e9de7755..e8efb735c 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul @@ -2,7 +2,7 @@ let a := mload(0) mstore(0, sub(a, a)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul b/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul index d75e681a4..73bd6c363 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul @@ -3,9 +3,9 @@ let b := add(7, sub(f(), 7)) mstore(b, 0) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f() -> a // { } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul b/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul index 339e744d7..9d4e52dc1 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul @@ -3,9 +3,9 @@ let a := 10 for { } iszero(eq(a, sub(x, calldataload(3)))) { a := add(a, 1) } {} } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let a := 10 // for { } iszero(iszero(a)) { a := add(a, 1) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul b/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul index 453f74e33..687491818 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul @@ -7,9 +7,9 @@ // run of CSE afterwards. mstore(b, eq(calldataload(0), a)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let a := calldataload(0) // let _4 := 0 diff --git a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul index 59dbf172b..1e448c9a4 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 8))) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _4 := 0 // mstore(_4, and(calldataload(_4), 255)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul index 41254b818..0756c1841 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul @@ -1,9 +1,9 @@ { mstore(0, mod(calldataload(0), exp(2, 255))) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _4 := 0 // mstore(_4, and(calldataload(_4), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul index 5038a6fa9..8dd271d9f 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul @@ -2,9 +2,9 @@ function f(a) -> b { } mstore(0, sub(f(0), f(1))) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f(a) -> b // { } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul index 72ebadd20..0b17b1e4a 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul @@ -4,9 +4,9 @@ let c := sub(f1(), f2()) mstore(0, c) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f1() -> a // { } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul index 11de59aba..3133c69ca 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul @@ -4,9 +4,9 @@ let b := sub(f(), f()) mstore(0, b) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // function f() -> a // { mstore(1, 2) } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul index 9f916198c..05fc9447d 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul @@ -3,9 +3,9 @@ let a := div(create(0, 0, 0), 0) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _1 := 0 // pop(create(_1, _1, _1)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul index 811f86780..93b424233 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul @@ -19,9 +19,9 @@ mstore(17, or(x, not(x))) mstore(18, or(not(x), x)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // mstore(1, 0) // mstore(2, 0) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul b/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul index fceec7ecf..5fe632495 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul @@ -2,9 +2,9 @@ let a := add(0, mload(0)) mstore(0, a) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // let _1 := 0 // mstore(_1, mload(_1)) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul index 13d4997dd..9ebdf0128 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul @@ -4,9 +4,9 @@ let y := 255 mstore(1, signextend(0, y)) } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { // mstore(0, 7) // mstore(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) diff --git a/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul b/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul index 7ff4c60c3..15ff14a84 100644 --- a/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul +++ b/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: fullSimplify // ---- +// step: fullSimplify +// // { } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 384a47d89..2f400186a 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1071,9 +1071,10 @@ } } // ==== -// step: fullSuite // EVMVersion: >=constantinople // ---- +// step: fullSuite +// // { // { // let _1 := mload(1) diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index 4557c1889..5e6e0305d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -458,8 +458,9 @@ } // ==== // EVMVersion: >=constantinople -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := 0 diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 635da72d6..1076f349a 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -228,9 +228,9 @@ mstore(0x00, keccak256(0x300, mul(n, 0x80))) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := 0x80 diff --git a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul index a9cdb84e0..71ed36d97 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul @@ -6,9 +6,9 @@ } if y { revert(0, 0) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let y := mload(0x20) diff --git a/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul b/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul index 314f2a363..c5cdf8104 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/devcon_example.yul @@ -14,9 +14,9 @@ v := calldataload(add(data, mul(i, 0x20))) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul b/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul index f8fb81aa8..3b30ac4f7 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/loopInvariantCodeMotion.yul @@ -17,9 +17,9 @@ v := add(v, calldataload(7)) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index c5ed25e2f..4f1a6f35d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -16,9 +16,9 @@ for { switch mul(1,2) case 2 { mstore(0x40, 0x20) } } sub(1,1) {} { mstore(0x80, 0x40) } } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let p := mload(0x40) diff --git a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul index 4efe81d89..f0ee054b9 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/no_move_loop_orig.yul @@ -7,9 +7,9 @@ } {} } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := iszero(caller()) diff --git a/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul b/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul index fa1364297..a0ac5c36d 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/remove_redundant_assignments_in_switch.yul @@ -6,9 +6,9 @@ default { invalid() } mstore(1, 1) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // switch mload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul b/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul index bb350bf8f..6e217ae4f 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/reuse_vars_bug_in_simplifier.yul @@ -9,7 +9,7 @@ mstore(sub(1,div(sub(x_9,1),sub(1,sub(x_9,1)))), 1) } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { { mstore(1, 1) } } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul index c4a70b03e..78af81f38 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul @@ -31,9 +31,9 @@ a,b := abi_decode_t_bytes_calldata_ptr(a,b) mstore(a,b) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul index 564ed9664..4421dd3cd 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul @@ -9,9 +9,9 @@ } mstore(a, b) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let a := mload(0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul index 6a4e52652..961abd72c 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul @@ -20,9 +20,9 @@ sstore(0,0) sstore(3,1) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // let _1 := gt(not(pc()), 1) diff --git a/test/libyul/yulOptimizerTests/fullSuite/storage.yul b/test/libyul/yulOptimizerTests/fullSuite/storage.yul index 4daff379f..7b3360847 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/storage.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/storage.yul @@ -3,9 +3,9 @@ sstore(4, 3) sstore(8, sload(4)) } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { // { // sstore(4, 5) diff --git a/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul b/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul index 2e878e437..65c7512d3 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/switch_inline.yul @@ -6,7 +6,7 @@ case 1 { y := 9 } } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { { mstore(9, 0) } } diff --git a/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul b/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul index 6710f1fee..cff8bd755 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/switch_inline_match_default.yul @@ -7,7 +7,7 @@ default { y := 10 } } } -// ==== -// step: fullSuite // ---- +// step: fullSuite +// // { { mstore(10, 0) } } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul b/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul index 95117c38e..b75efc54c 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/already_grouped.yul @@ -4,9 +4,9 @@ } function f() -> y { y := 8 } } -// ==== -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { let x := 2 } // function f() -> y diff --git a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul index f08de996b..7bfbb3401 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul @@ -1,8 +1,9 @@ { let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { // let a diff --git a/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul b/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul index 4ad0c872d..2e5380d2c 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/grouped_but_not_ordered.yul @@ -4,9 +4,9 @@ let x := 2 } } -// ==== -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { { let x := 2 } } // function f() -> y diff --git a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul index 5fb398ad7..1aedecf6a 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul @@ -6,8 +6,9 @@ } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { // let a diff --git a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul index 455da2ef5..9193da739 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul @@ -10,8 +10,9 @@ } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { let a } // function f() diff --git a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul index f7836b6c6..b007f1855 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul @@ -3,8 +3,9 @@ } // ==== // dialect: yul -// step: functionGrouper // ---- +// step: functionGrouper +// // { // { let a } // function f() diff --git a/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul index 00895bf38..e292340f2 100644 --- a/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul +++ b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: functionGrouper // ---- +// step: functionGrouper +// // { { } } diff --git a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul index 276fd4b02..bd66b3a2b 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // function f() -> x:bool diff --git a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul index 4e9e9604f..060fab314 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // let c diff --git a/test/libyul/yulOptimizerTests/functionHoister/nested.yul b/test/libyul/yulOptimizerTests/functionHoister/nested.yul index 89ea5c0ff..05eb4803b 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/nested.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/nested.yul @@ -8,8 +8,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // function g() diff --git a/test/libyul/yulOptimizerTests/functionHoister/single.yul b/test/libyul/yulOptimizerTests/functionHoister/single.yul index 6cc82e68a..205626b57 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/single.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/single.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: functionHoister // ---- +// step: functionHoister +// // { // let a // function f() diff --git a/test/libyul/yulOptimizerTests/functionHoister/smoke.yul b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul index 27c9e8419..e0d43adbb 100644 --- a/test/libyul/yulOptimizerTests/functionHoister/smoke.yul +++ b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul @@ -1,6 +1,6 @@ { } -// ==== -// step: functionHoister // ---- +// step: functionHoister +// // { } diff --git a/test/libyul/yulOptimizerTests/loadResolver/loop.yul b/test/libyul/yulOptimizerTests/loadResolver/loop.yul index 4f7b27734..3606891ee 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/loop.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/loop.yul @@ -4,9 +4,9 @@ x := add(x, 1)} {y := add(x, y) } } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 123213 // let _2 := 0 diff --git a/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul b/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul index a9487afa7..77fc5aef0 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/memory_with_different_kinds_of_invalidation.yul @@ -14,9 +14,9 @@ function g() {} } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 9 // let _2 := 2 diff --git a/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul b/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul index 17a8761d9..199fb1f6c 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/memory_with_msize.yul @@ -5,9 +5,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := msize() // let _3 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul index 7f5dfb2f0..e9ced5f4c 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write.yul @@ -7,9 +7,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // let _3 := 0 diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul index f7cfdd7c3..8ce9fba00 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_known_write_with_distance.yul @@ -7,9 +7,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // let _4 := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul index 4fc21bbf5..225e0f627 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_unknown_write.yul @@ -7,9 +7,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // let _3 := 0 diff --git a/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul b/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul index ebe65992a..b4b113e81 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/merge_with_rewrite.yul @@ -7,9 +7,9 @@ } sstore(0, mload(2)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 3 // let _2 := 2 diff --git a/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul b/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul index 9bb8261dd..5e5c06553 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/mload_in_function.yul @@ -6,9 +6,9 @@ foo(42) sstore(0, mload(0)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function foo(x) // { diff --git a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul index 2de873942..da910bace 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_body.yul @@ -11,9 +11,9 @@ funcWithLoop(42) sstore(0, mload(0)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function userNot(x) -> y // { y := iszero(x) } diff --git a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul index 1e06cf8df..efef507dc 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/mstore_in_function_loop_init.yul @@ -11,9 +11,9 @@ funcWithLoop(42) sstore(0, mload(0)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function userNot(x) -> y // { y := iszero(x) } diff --git a/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul b/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul index 7fb27c996..01e29b705 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/re_store_memory.yul @@ -7,9 +7,9 @@ mstore(a, c) sstore(10, mload(a)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let a := 0 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul b/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul index aed91e002..8c33e4f5d 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/re_store_storage.yul @@ -7,9 +7,9 @@ sstore(a, c) mstore(32, sload(a)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let a := 0 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/loadResolver/reassign.yul b/test/libyul/yulOptimizerTests/loadResolver/reassign.yul index fe7fccfe6..4687422e9 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/reassign.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/reassign.yul @@ -4,9 +4,9 @@ a := calldataload(2) mstore(0, sload(a)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _1 := 0 // let a := calldataload(_1) diff --git a/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul b/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul index d2d0999fa..1b73cecb9 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/reassign_value_expression.yul @@ -14,9 +14,9 @@ a := 39 mstore(sload(a), 11) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let a := add(x, 10) diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul b/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul index ae23c1b8b..1aa05106c 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_mstore_with_delta.yul @@ -9,9 +9,9 @@ mstore(b, 8) sstore(mload(a), mload(b)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let a := add(x, 10) diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_store.yul b/test/libyul/yulOptimizerTests/loadResolver/second_store.yul index 67aa196f4..f6f8250ce 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_store.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_store.yul @@ -6,9 +6,9 @@ // if the two slots are different. mstore(0, sload(x)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // sstore(x, 7) diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul b/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul index b90b177b3..a5cb459ad 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_store_same_value.yul @@ -6,9 +6,9 @@ // written are 7. mstore(0, sload(x)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let _2 := 7 diff --git a/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul b/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul index a4154ce63..c2e669b7f 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/second_store_with_delta.yul @@ -9,9 +9,9 @@ sstore(b, 8) mstore(sload(a), sload(b)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let x := calldataload(1) // let a := add(x, 10) diff --git a/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul b/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul index b07883c3a..ec9eec6c9 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/side_effects_of_user_functions.yul @@ -8,9 +8,9 @@ stores() sstore(0, mload(2)) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // function stores() // { mstore(0, 1) } diff --git a/test/libyul/yulOptimizerTests/loadResolver/simple.yul b/test/libyul/yulOptimizerTests/loadResolver/simple.yul index a32a54241..a645f1692 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/simple.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/simple.yul @@ -4,9 +4,9 @@ let q := sload(calldataload(0)) mstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // sstore(calldataload(0), _2) diff --git a/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul b/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul index a4c06ebdc..667abe053 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/simple_memory.yul @@ -4,9 +4,9 @@ let q := mload(calldataload(0)) sstore(t, q) } -// ==== -// step: loadResolver // ---- +// step: loadResolver +// // { // let _2 := calldataload(10) // mstore(calldataload(0), _2) diff --git a/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul b/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul index 75307ad5d..b24ab0170 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/staticcall.yul @@ -10,9 +10,10 @@ mstore(0, sload(a)) } // ==== -// step: loadResolver // EVMVersion: >=byzantium // ---- +// step: loadResolver +// // { // let a := 0 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul index 82ea2bf78..a1b58ceb2 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/dependOnVarInLoop.yul @@ -7,9 +7,9 @@ mstore(a, not_inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul index c7e39cc70..19a85c813 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/multi.yul @@ -9,9 +9,9 @@ mstore(a, inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul index 7f842bbdd..19e681d6b 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_loop.yul @@ -8,9 +8,9 @@ let q := g() } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // function f() -> x // { x := g() } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul index 5f3eeb7ac..2bb86008e 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_recursive_function.yul @@ -8,9 +8,9 @@ let q := g() } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // function f() -> x // { x := g() } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul index 86cf1274e..18feab0c1 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/non-ssavar.yul @@ -7,9 +7,9 @@ mstore(a, not_inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul index 787c1756b..15fe39ccd 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/nonMovable.yul @@ -6,9 +6,9 @@ mstore(a, inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 0 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul index a489b134b..5fdabc7cb 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/recursive.yul @@ -8,9 +8,9 @@ a := add(a, 1) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul index 970fec59f..b0527e2b6 100644 --- a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple.yul @@ -6,9 +6,9 @@ mstore(a, inv) } } -// ==== -// step: loopInvariantCodeMotion // ---- +// step: loopInvariantCodeMotion +// // { // let b := 1 // let a := 1 diff --git a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul index 741efd42b..635e618d5 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul @@ -9,8 +9,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { diff --git a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul index 69fb0bef0..1d14937e7 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul @@ -7,8 +7,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { diff --git a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul index b97bb2f4d..a888970e7 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul @@ -8,8 +8,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { let a } diff --git a/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul index 1ab108f6b..6b20d712c 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/single_fun.yul @@ -4,8 +4,9 @@ } // ==== // dialect: yul -// step: mainFunction // ---- +// step: mainFunction +// // { // function main() // { let a } diff --git a/test/libyul/yulOptimizerTests/mainFunction/smoke.yul b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul index 54ad2d843..5bad02dfb 100644 --- a/test/libyul/yulOptimizerTests/mainFunction/smoke.yul +++ b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul @@ -1,8 +1,9 @@ {} // ==== -// step: mainFunction // dialect: yul // ---- +// step: mainFunction +// // { // function main() // { } diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul b/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul index 9c547d17a..54045ce37 100644 --- a/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul +++ b/test/libyul/yulOptimizerTests/nameDisplacer/funtion_call.yul @@ -6,9 +6,9 @@ function illegal5(illegal1, illegal2) -> illegal3 { illegal3 := add(illegal1, illegal2) } } } -// ==== -// step: nameDisplacer // ---- +// step: nameDisplacer +// // { // let x := illegal4_1(1, 2) // function illegal4_1(illegal1_2, illegal2_3) -> illegal3_4 diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul b/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul index dae343217..08b7bc431 100644 --- a/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul +++ b/test/libyul/yulOptimizerTests/nameDisplacer/variables.yul @@ -1,7 +1,7 @@ { { let illegal1 := 1 } { let illegal2 := 2 let illegal3, illegal4 } } -// ==== -// step: nameDisplacer // ---- +// step: nameDisplacer +// // { // { let illegal1_1 := 1 } // { diff --git a/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul index 73228739a..472aa6a33 100644 --- a/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul +++ b/test/libyul/yulOptimizerTests/nameDisplacer/variables_inside_functions.yul @@ -4,9 +4,9 @@ illegal3 := add(illegal1, illegal2) } } -// ==== -// step: nameDisplacer // ---- +// step: nameDisplacer +// // { // function f(illegal1_1, illegal2_2) -> illegal3_3 // { diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul index a7280364f..314963368 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul @@ -7,9 +7,9 @@ a := 7 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a := 2 // a := 3 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul index 6b15425c9..120b43ce8 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul @@ -13,9 +13,9 @@ y := 8 mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // let y diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul index 647de0d24..1b795ad8c 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul @@ -12,9 +12,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul index 3ac614b67..0ee9c551d 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul @@ -15,9 +15,9 @@ } x := 3 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // for { } calldataload(0) { } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul index ab7ba0df1..ee747ae5d 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul @@ -12,9 +12,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul index 14faa2a75..b70f35d2b 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul @@ -11,9 +11,9 @@ x := 3 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // for { } calldataload(0) { mstore(x, 0x42) } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul index 87104647d..f54b21af8 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_decl_inside_break_continue.yul @@ -18,9 +18,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // for { } calldataload(0) { } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul index 96571f435..5edd71d58 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_noremove.yul @@ -25,9 +25,9 @@ } x := 13 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 2 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul index 6df6e33ef..5b086bab8 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_deep_simple.yul @@ -18,9 +18,9 @@ } } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // for { } 1 { } // { diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul index 5b0e70f8c..ce084b1ce 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_multi_break.yul @@ -34,9 +34,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul index 7e9518f89..a83f53666 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_nested.yul @@ -31,9 +31,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul index 3cf202582..5d1e60738 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul @@ -10,9 +10,9 @@ } x := 3 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul index eceba8c23..5e88df968 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_stmnts_after_break_continue.yul @@ -22,9 +22,9 @@ } mstore(x, 0x42) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x := 1 // let y := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul index 444f73265..2a20ca8b6 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul @@ -11,9 +11,9 @@ } r := 2 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let r // function f(x, y) -> a, b diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul index 35b6c968f..2fab04a36 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul @@ -9,9 +9,9 @@ // This enforces that none of the assignments above can be removed. mstore(0, d) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul index 120c3e07b..7585f848a 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul @@ -10,9 +10,9 @@ d := 3 mstore(0, d) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul index 9fadcbbe9..637a32c02 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul @@ -10,9 +10,9 @@ d := 3 mstore(0, d) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let c // let d diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul index ef68155ad..1caee198a 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/leave.yul @@ -20,9 +20,9 @@ t := 8 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // function f(a, b) -> x // { diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul index b4546aa55..f0845680f 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul @@ -8,9 +8,9 @@ x := 3 y := 4 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // function f() -> a, b // { } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul index b094f25b7..2b13ba3c3 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul @@ -5,9 +5,9 @@ b := a a := b } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a := 2 // a := 7 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul index a5e697555..343c672de 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul @@ -3,9 +3,9 @@ a := 0 a := mload(0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a // a := mload(0) diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul index 8e75d82ed..035f66d38 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_break.yul @@ -11,9 +11,9 @@ mstore(0, x) } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let i := 0 // for { } lt(i, 2) { i := add(i, 1) } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul index f5a29382b..c4f3b3e43 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/remove_continue.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let i := 0 // for { } lt(i, 2) { i := add(i, 1) } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul index 85c83bde8..d3c6cb4a6 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul @@ -6,9 +6,9 @@ a := 2 } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let a // { let b } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul index 1fd827ed1..12b9ae6b7 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul @@ -3,7 +3,7 @@ a := 1 a := 2 } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { let a } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul index 547ae025b..a39cdedd7 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul @@ -7,9 +7,9 @@ default { x := 3 } mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul index 7ab2036e1..271bb4864 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul @@ -6,9 +6,9 @@ case 0 { x := 2 } mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul index 8dc43d721..8d38dcc16 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul @@ -7,9 +7,9 @@ default { mstore(x, 1) } mstore(x, 0) } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // x := 1 diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul index f0b29f301..94de51b88 100644 --- a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul @@ -5,9 +5,9 @@ switch calldataload(0) case 0 { mstore(0, 1) } } -// ==== -// step: redundantAssignEliminator // ---- +// step: redundantAssignEliminator +// // { // let x // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul index 520c87b92..39a7f91b3 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul @@ -5,9 +5,9 @@ pop(a) } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // pop(caller()) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul index 4dd86f958..59731d617 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul @@ -7,9 +7,9 @@ } let x := a } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // pop(caller()) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul index e664291f5..0a1e38fa6 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul @@ -4,9 +4,9 @@ if b { pop(b) b := a } let c := b } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // let b := address() diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul index e0aa0a385..442d64654 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul @@ -6,9 +6,9 @@ default { let x := a let y := b b := a } pop(add(a, b)) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // let b := 2 diff --git a/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul b/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul index 95733af88..c3f6ea6a2 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/cheap_caller.yul @@ -6,9 +6,9 @@ mstore(add(a, a), mload(a)) sstore(a, sload(a)) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := caller() // mstore(caller(), caller()) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul index 074100d89..7fc927a4a 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul @@ -7,9 +7,9 @@ } let b := x } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x // { diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul index cc009509f..6221f1160 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_remat_large_amounts_of_code_if_used_once.yul @@ -2,9 +2,9 @@ let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) let b := x } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) // let b := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul index e39b66b44..272160003 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_break.yul @@ -13,9 +13,9 @@ } mstore(a, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul index 5a98c4c4d..ed80715e9 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue.yul @@ -15,9 +15,9 @@ } mstore(a, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul index f01514cc5..a5d8198b0 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_2.yul @@ -20,9 +20,9 @@ } mstore(a, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul index ae04949d9..1b9b3a84a 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/for_continue_with_assignment_in_post.yul @@ -23,9 +23,9 @@ let x := b // does not rematerialize as b may be either origin() or callvalue() (btw: not caller()) let y := c // does not rematerialize as c may be either origin() or caller() } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul b/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul index 9f57a4739..b00d50f26 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/large_constant.yul @@ -4,9 +4,9 @@ let a := 0xffffffffffffffffffffff mstore(a, a) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 0xffffffffffffffffffffff // mstore(a, a) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul b/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul index 6d388c19e..cc8d23398 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/large_constant_used_once.yul @@ -5,9 +5,9 @@ let a := 0xffffffffffffffffffffff mstore(0, a) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 0xffffffffffffffffffffff // mstore(0, 0xffffffffffffffffffffff) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul index 0d42afa97..92aacb229 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/many_refs_small_cost_loop.yul @@ -12,9 +12,9 @@ let c := sdiv(x, 4) } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := 0xff // for { } lt(x, 0x100) { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul b/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul index ec79843b1..fb73227b7 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/medium_sized_constant.yul @@ -12,9 +12,9 @@ mstore(add(a, a), a) mstore(a, mload(a)) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let b := 2 // mstore(2, 2) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul index 8fdd1f002..4255445b3 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/no_remat_in_loop.yul @@ -18,9 +18,9 @@ } } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := origin() // let b := calldataload(0) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul index 1b59fd598..4cffa6a02 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul @@ -5,9 +5,9 @@ let c := a mstore(add(a, b), c) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // function f(x) -> y // { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul index 0fb885604..dc1d78c74 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul @@ -4,9 +4,9 @@ let c := a mstore(add(a, b), c) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul index fe3cf5812..ef6e12ab6 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul @@ -6,9 +6,9 @@ let d := add(b, c) pop(a) pop(b) pop(c) pop(d) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := extcodesize(0) // let b := a diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul index c10f9ddf2..7af26af63 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul @@ -5,9 +5,9 @@ let b := mload(a) pop(b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // pop(1) diff --git a/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul index e0452c4b8..e7c28202c 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul @@ -1,5 +1,5 @@ {} -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul index 0eeb248a6..071c3c463 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_loop.yul @@ -7,9 +7,9 @@ let y := add(x, 1) } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := 0xff // for { } lt(x, 0x100) { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul index c95cf5454..3d4d65f0a 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/some_refs_small_cost_nested_loop.yul @@ -12,9 +12,9 @@ } } } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let x := 0xff // for { } lt(x, 0x100) { } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul index 35d4f0332..04a797e20 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul @@ -3,9 +3,9 @@ let b := a mstore(0, b) } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := 1 // let b := 1 diff --git a/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul index 076131cde..bb5f02980 100644 --- a/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul +++ b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul @@ -4,9 +4,9 @@ a := mul(a, 2) let b := a } -// ==== -// step: rematerialiser // ---- +// step: rematerialiser +// // { // let a := extcodesize(0) // a := mul(a, 2) diff --git a/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul b/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul index 7e60a54be..c8b22424c 100644 --- a/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul +++ b/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul @@ -6,9 +6,9 @@ } } } -// ==== -// step: splitJoin // ---- +// step: splitJoin +// // { // if mul(add(calldataload(0), 2), 3) // { diff --git a/test/libyul/yulOptimizerTests/splitJoin/functions.yul b/test/libyul/yulOptimizerTests/splitJoin/functions.yul index b4ad9ed8e..e2f554dd0 100644 --- a/test/libyul/yulOptimizerTests/splitJoin/functions.yul +++ b/test/libyul/yulOptimizerTests/splitJoin/functions.yul @@ -8,9 +8,9 @@ sstore(b, mul(b, 2)) } } -// ==== -// step: splitJoin // ---- +// step: splitJoin +// // { // let x := f(0) // function f(y) -> r diff --git a/test/libyul/yulOptimizerTests/splitJoin/smoke.yul b/test/libyul/yulOptimizerTests/splitJoin/smoke.yul index d59bd5644..4aac31ddb 100644 --- a/test/libyul/yulOptimizerTests/splitJoin/smoke.yul +++ b/test/libyul/yulOptimizerTests/splitJoin/smoke.yul @@ -1,5 +1,5 @@ {} -// ==== -// step: splitJoin // ---- +// step: splitJoin +// // { } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul index e4787540d..43b9ade6a 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul @@ -15,9 +15,9 @@ b := mload(a) } } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul index 0617c47e4..2e818561c 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul @@ -6,9 +6,9 @@ a := mload(4) mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a_5 := mload(4) // mstore(a_5, 0) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul index 73aee8516..4815349d7 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul @@ -8,9 +8,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // if mload(1) { a := mload(3) } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul index b6eb0b6cf..a0821ab37 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul @@ -9,9 +9,9 @@ } mstore(a, b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul index e747e8503..92a0ba825 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul @@ -16,9 +16,9 @@ } mstore(a, b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // let b := mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul index 8265f500e..63c4f8561 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul @@ -13,9 +13,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // switch mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul index 15e56f4d6..e2f130507 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul @@ -3,9 +3,9 @@ a := mload(1) mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a_2 := mload(1) // mstore(a_2, 0) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul index 0e777a5a5..e2516b45b 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul @@ -6,9 +6,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // if mload(1) { a := mload(1) } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul index aacad246a..e6e0553c1 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul @@ -9,9 +9,9 @@ } mstore(a, 0) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a := mload(0) // switch mload(1) diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul index fbb149c68..da8d797b8 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul @@ -20,9 +20,9 @@ let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) mstore(a,b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 // { diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul index 1c246a754..6053bd9ac 100644 --- a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul +++ b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul @@ -7,9 +7,9 @@ b := mload(a) mstore(a, b) } -// ==== -// step: ssaAndBack // ---- +// step: ssaAndBack +// // { // let a_1 := mload(0) // let b_2 := mload(a_1) diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul index 28520d3c5..104dc2822 100644 --- a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul @@ -10,9 +10,9 @@ } } } -// ==== -// step: ssaPlusCleanup // ---- +// step: ssaPlusCleanup +// // { // function copy(from, to) -> length // { diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul index 105970c3c..85e86e005 100644 --- a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul @@ -5,9 +5,9 @@ a := 4 mstore(0, a) } -// ==== -// step: ssaPlusCleanup // ---- +// step: ssaPlusCleanup +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul index 6770fee2b..75a045d05 100644 --- a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul @@ -5,9 +5,9 @@ a := mload(add(a, 4)) mstore(0, a) } -// ==== -// step: ssaPlusCleanup // ---- +// step: ssaPlusCleanup +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul index 18d404df0..1c79777d2 100644 --- a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul +++ b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul @@ -19,9 +19,9 @@ } } } -// ==== -// step: ssaReverser // ---- +// step: ssaReverser +// // { // function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 // { diff --git a/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul b/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul index 48b9d39cf..1985f6c08 100644 --- a/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul +++ b/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul @@ -2,7 +2,7 @@ let a := calldataload(0) a := a } -// ==== -// step: ssaReverser // ---- +// step: ssaReverser +// // { let a := calldataload(0) } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul index 03dab25f9..be2cfe9d7 100644 --- a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul @@ -4,9 +4,9 @@ a := a_1 mstore(a_1, 0) } -// ==== -// step: ssaReverser // ---- +// step: ssaReverser +// // { // let a := mload(1) // a := mload(0) diff --git a/test/libyul/yulOptimizerTests/ssaTransform/branches.yul b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul index 76a459160..81f82451f 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/branches.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul @@ -7,9 +7,9 @@ a := add(a, 1) mstore(a, 1) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul index 6901f9f17..e1e1c13ba 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul @@ -6,9 +6,9 @@ } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul index fcf1a4478..284bdbf3d 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul @@ -6,9 +6,9 @@ } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul index 217a043ad..ac7e426ab 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul @@ -6,9 +6,9 @@ } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul index cec419633..77ac89a60 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul @@ -13,9 +13,9 @@ } a := add(a, 8) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/function.yul b/test/libyul/yulOptimizerTests/ssaTransform/function.yul index b6e120f40..d41c18b04 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/function.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/function.yul @@ -6,9 +6,9 @@ a := add(a, d) } } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // function f(a, b) -> c, d // { diff --git a/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul b/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul index 4f1c44123..ff4a0eb1d 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/multi_assign.yul @@ -7,9 +7,9 @@ b := mload(a) function f() -> x, y {} } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul b/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul index ea849ead9..06847711e 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/multi_decl.yul @@ -6,9 +6,9 @@ sstore(a, b) function f(t, v) -> w, z {} } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let x_1, y_2 := f(1, 2) // let x := x_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/nested.yul b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul index a48de0002..e075f06a2 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/nested.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul @@ -10,9 +10,9 @@ } a := add(b, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul b/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul index 7d2d7bc33..32d74f180 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/nested_reassign.yul @@ -11,9 +11,9 @@ // but not above because end of block mstore(0, x) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul index f42de9976..b41998c59 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul @@ -6,9 +6,9 @@ mstore(c, 0) c := add(a, b) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a := 1 // let b := add(a, 2) diff --git a/test/libyul/yulOptimizerTests/ssaTransform/simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul index d23d07d82..b52ec0c05 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/simple.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul @@ -4,9 +4,9 @@ a := 3 a := 4 } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul index 32d6b339b..8604be0e3 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/switch.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul @@ -8,9 +8,9 @@ default { a := add(a, 8) } mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul b/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul index 775df55f4..3cf38ef38 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/switch_reassign.yul @@ -6,9 +6,9 @@ // should still create an SSA variable for a mstore(0, a) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := mload(0) // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed.yul index f9d48a227..4b1aa1643 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/typed.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed.yul @@ -14,8 +14,9 @@ } // ==== // dialect: evmTyped -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let b_1:bool := true // let b:bool := b_1 diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul index 685c42a24..596f56cb0 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed_for.yul @@ -8,8 +8,9 @@ } // ==== // dialect: evmTyped -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let b:bool := true // let c_1:bool := false diff --git a/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul index 7d61b1031..b87b5dbbc 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/typed_switch.yul @@ -8,8 +8,9 @@ } // ==== // dialect: evmTyped -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let b:bool := true // let c_1:bool := false diff --git a/test/libyul/yulOptimizerTests/ssaTransform/used.yul b/test/libyul/yulOptimizerTests/ssaTransform/used.yul index c23bc6ee8..cbd0cdb25 100644 --- a/test/libyul/yulOptimizerTests/ssaTransform/used.yul +++ b/test/libyul/yulOptimizerTests/ssaTransform/used.yul @@ -14,9 +14,9 @@ a := 4 mstore(a, 0) } -// ==== -// step: ssaTransform // ---- +// step: ssaTransform +// // { // let a_1 := 1 // let a := a_1 diff --git a/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul b/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul index 6c0ceef63..4ff056157 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/inlineInBlock.yul @@ -3,9 +3,9 @@ let y := calldataload(calldataload(9)) mstore(y, add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(y, 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // mstore(calldataload(calldataload(9)), add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(calldataload(calldataload(9)), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) // } diff --git a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul index e74e788aa..f237ea8bb 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/inlineInFunction.yul @@ -5,9 +5,9 @@ mstore(y, add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(add(y, 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1), 1)) } } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // let x := 8 // function f() diff --git a/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul b/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul index 39c46fb96..b5f3dd872 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/noInline.yul @@ -2,9 +2,9 @@ let x := 8 function f() { let y := 9 } } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // let x := 8 // function f() diff --git a/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul b/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul index f285d6b5e..279c0d901 100644 --- a/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul +++ b/test/libyul/yulOptimizerTests/stackCompressor/unusedPrunerWithMSize.yul @@ -18,9 +18,9 @@ extcodecopy(1, msize(), 1, 1) } } -// ==== -// step: stackCompressor // ---- +// step: stackCompressor +// // { // let _17_72 := pc() // let _22_75 := pc() diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul index f180d50ad..94da17faa 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/bugfix_visit_after_change.yul @@ -6,7 +6,7 @@ x := 1 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let x := 0 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol b/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.yul similarity index 93% rename from test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol rename to test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.yul index 0d352a3f9..edca6b642 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.sol +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/for_false_condition.yul @@ -3,7 +3,7 @@ let b := a } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let a := 42 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul index ab08b18e8..3b7a39181 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_false_condition.yul @@ -1,5 +1,5 @@ { if 0 { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul index 836b4371c..bbed85bb0 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_multi_unassigned_condition.yul @@ -3,7 +3,7 @@ if x { mstore(0, 0) } if y { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let x, y } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul index 7d493efbc..bf1046fb2 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_true_condition.yul @@ -1,5 +1,5 @@ { if 1 { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul index 4595fe899..bee5381c8 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/if_unassigned_condition.yul @@ -2,7 +2,7 @@ let x if x { mstore(0, 0) } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let x } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul index 6783b6eee..44b2ecc61 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/nested.yul @@ -1,5 +1,5 @@ { if 1 { if 1 { for { mstore(0, 0) } 0 {} { mstore(2, 3) } if 0 { mstore(1, 2) } } } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { mstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul index b80d4ea99..faa0304df 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline.yul @@ -4,9 +4,9 @@ case 0 { y := 8 } case 1 { y := 9 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { // let y := 200 // { y := 9 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul index 5e985d78e..9238cb057 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_match_default.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } default { y := 10 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { // let y := 200 // { y := 10 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul index 584bbb73d..9036db709 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match.yul @@ -4,7 +4,7 @@ case 0 { y := 8 } case 1 { y := 9 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let y := 200 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul index 0dea12292..5eeae71ba 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_inline_no_match_mixed.yul @@ -5,7 +5,7 @@ case "" { y := 8 } case 1 { y := 9 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { let y := 200 } diff --git a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul index 8629be3f6..f8cc0558a 100644 --- a/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul +++ b/test/libyul/yulOptimizerTests/structuralSimplifier/switch_no_remove_empty_case.yul @@ -5,9 +5,9 @@ case 1 { y := 9 } default { y := 100 } } -// ==== -// step: structuralSimplifier // ---- +// step: structuralSimplifier +// // { // let y := 200 // switch calldataload(0) diff --git a/test/libyul/yulOptimizerTests/unusedPruner/functions.yul b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul index 80758f082..cceace3bb 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/functions.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul @@ -2,7 +2,7 @@ function f() { let a := 1 } function g() { f() } } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul index 1c1397061..32e96b9f3 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul @@ -3,9 +3,9 @@ a := 4 let b := 1 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let a := 1 // a := 4 diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul index 2dd207fbf..bc05f09f8 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul @@ -4,9 +4,9 @@ a := f() b := 1 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let a, b // function f() -> x diff --git a/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul b/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul index c6b93a648..6ad93184a 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/keccak.yul @@ -3,9 +3,9 @@ let b := keccak256(1, 1) sstore(0, msize()) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // pop(keccak256(1, 1)) // sstore(0, msize()) diff --git a/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul b/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul index 0d49d713b..09d366fb2 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/movable_user_defined_function.yul @@ -7,7 +7,7 @@ } let x := f(g(2)) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/msize.yul b/test/libyul/yulOptimizerTests/unusedPruner/msize.yul index ae6beaa75..05ad7b743 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/msize.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/msize.yul @@ -3,9 +3,9 @@ let b := mload(10) sstore(0, msize()) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // pop(mload(10)) // sstore(0, msize()) diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul index 77e9306c9..47e114373 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul @@ -4,9 +4,9 @@ function f() -> x, y { } a, b := f() } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let a // let b diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul index 01e573199..86f175d69 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul @@ -3,9 +3,9 @@ x := 1 y := 2 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let x, y // x := 1 diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul index 76bd6a510..f727b0e93 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul @@ -1,7 +1,7 @@ { let x, y } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul index b4f6a5a7e..aa8ae0ad4 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul @@ -2,7 +2,7 @@ function f() -> x, y { } let a, b := f() } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul index 67d692f70..f54087a7d 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul @@ -2,9 +2,9 @@ let x, y x := 1 } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { // let x, y // x := 1 diff --git a/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul b/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul index 8d40256eb..7bfc4d788 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/no_msize.yul @@ -3,7 +3,7 @@ let b := mload(10) sstore(0, 5) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { sstore(0, 5) } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/pop.yul b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul index 089098215..66d16db1e 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/pop.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul @@ -2,7 +2,7 @@ let a := 1 pop(a) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul index 9adf68be5..a2ac495a0 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul @@ -1,5 +1,5 @@ { } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul index b3a05d38f..9de594351 100644 --- a/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul +++ b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul @@ -3,7 +3,7 @@ let b := 1 mstore(0, 1) } -// ==== -// step: unusedPruner // ---- +// step: unusedPruner +// // { mstore(0, 1) } diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul index 054d0b53c..8d306ca16 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/ambiguous.yul @@ -11,9 +11,9 @@ let b := 2 let x, y := f() } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // function f() -> x, y // { diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul index f9510e32f..9a169b435 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/inside_func.yul @@ -8,9 +8,9 @@ let r r := 4 } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // function f() -> x, y // { diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul index 4911a6bd2..4968e2b04 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/multi.yul @@ -3,9 +3,9 @@ let a let b } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // let x := 0 // let y := 0 diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul index fac0eaa06..f8c37f2c5 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/multi_assign.yul @@ -7,9 +7,9 @@ let s := 3 let t } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // function f() -> x, y // { diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul index 62d21dd0f..2b7d1048f 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/simple.yul @@ -1,7 +1,7 @@ { let a } -// ==== -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { let a := 0 } diff --git a/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul b/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul index 912246bea..069c68cab 100644 --- a/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul +++ b/test/libyul/yulOptimizerTests/varDeclInitializer/typed.yul @@ -8,8 +8,9 @@ } // ==== // dialect: evmTyped -// step: varDeclInitializer // ---- +// step: varDeclInitializer +// // { // let a1 := 0 // let a2:bool := false diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul index 694d21847..b6ee0377f 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul @@ -1,7 +1,7 @@ { let datasize_256 := 1 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { let datasize_1 := 1 } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul index 629b5451e..ea4ca9ea2 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul @@ -3,9 +3,9 @@ function f() { let f_1 } let f_10 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let f_1 // function f() diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul index 6f1d1817c..1608dd6fb 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul @@ -8,9 +8,9 @@ } let f_10 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let f_1 // function f(x) -> x_1, y diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul index 7dabb4d47..c7d86ef98 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul @@ -2,9 +2,9 @@ function f() { let x_1 := 0 } function g() { let x_2 := 0 } } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // function f() // { let x := 0 } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul index acf71b1d9..2d7dbeb4a 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul @@ -1,7 +1,7 @@ { let mul_256 := 1 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { let mul_1 := 1 } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul index 4104c9567..cd32d8530 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul @@ -4,9 +4,9 @@ let a_4312 := 0xdeadbeef let _42 := 21718 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let a := 1 // let a_1 := 2 diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul index d96a7b0be..100e5ccc6 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul @@ -4,9 +4,9 @@ let x_2 := 3 let x_1 := 4 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let x := 1 // let x_1 := 2 diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul index e1132ad8b..d197d4ff3 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul @@ -3,9 +3,9 @@ let x_2 := 2 let x_3 := 3 } -// ==== -// step: varNameCleaner // ---- +// step: varNameCleaner +// // { // let x := 1 // let x_1 := 2 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul index fadfbd61f..10f8814a6 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/constant_assignment.yul @@ -3,9 +3,9 @@ val := 9876543219876543219876543219876543219876543219876543219876543219876543210 } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let val_0 := 196678011949 // let val_1 := 17592899865401375162 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul index d633e9500..3aef8c99c 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/function_call.yul @@ -12,9 +12,9 @@ } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // function swap(x_0, x_1, x_2, x_3, y_0, y_1, y_2, y_3) -> a_0, a_1, a_2, a_3, b_0, b_1, b_2, b_3 // { diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul index c1a99ef34..5ac0bfc00 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/functional_instruction.yul @@ -1,9 +1,9 @@ { let x := add(999999999999999999999999999999999999999999999999999999999999999, 77777777777777777777777777777777777777777777777777777777777777) } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 12390 // let _1_1 := 13186919961226471680 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul index 5bfef91b1..b799760f8 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/if.yul @@ -2,9 +2,9 @@ if calldataload(0) { sstore(0, 1) } if add(calldataload(0), calldataload(1)) { sstore(0, 2) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul index 016dd9bb8..803b28970 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/or_bool_renamed.yul @@ -2,9 +2,9 @@ let or_bool := 2 if or_bool { sstore(0, 1) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let or_bool_3_0 := 0 // let or_bool_3_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul index 22cb7631e..d40e2283f 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_1.yul @@ -5,9 +5,9 @@ case 2 { sstore(2, 1) } case 3 { sstore(3, 1) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul index 12759e230..a9612a392 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_2.yul @@ -5,9 +5,9 @@ case 0x01000000000000000000000000000000000000020 { sstore(2, 1) } case 0x02000000000000000000000000000000000000020 { sstore(3, 1) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul index 617eebb64..403c57962 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_3.yul @@ -6,9 +6,9 @@ case 3 { sstore(3, 1) } default { sstore(8, 9) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul index 20f81d614..d1f45a623 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_4.yul @@ -6,9 +6,9 @@ case 0x02000000000000000000000000000000000000020 { sstore(3, 1) } default { sstore(8, 9) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul index 2a42adefe..a02243a25 100644 --- a/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul +++ b/test/libyul/yulOptimizerTests/wordSizeTransform/switch_5.yul @@ -2,9 +2,9 @@ switch calldataload(0) default { sstore(8, 9) } } -// ==== -// step: wordSizeTransform // ---- +// step: wordSizeTransform +// // { // let _1_0 := 0 // let _1_1 := 0 diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 5ea8486ec..c8add4ce7 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable(isoltest ../CommonSyntaxTest.cpp ../EVMHost.cpp ../TestCase.cpp + ../TestCaseReader.cpp ../libsolidity/util/BytesUtils.cpp ../libsolidity/util/ContractABIUtils.cpp ../libsolidity/util/TestFileParser.cpp diff --git a/test/tools/afl_fuzzer.cpp b/test/tools/afl_fuzzer.cpp index 1ca5428e1..eff4bc55a 100644 --- a/test/tools/afl_fuzzer.cpp +++ b/test/tools/afl_fuzzer.cpp @@ -124,7 +124,7 @@ Allowed options)", else if (arguments.count("standard-json")) FuzzerUtil::testStandardCompiler(input, quiet); else - FuzzerUtil::testCompiler(input, optimize, quiet); + FuzzerUtil::testCompilerJsonInterface(input, optimize, quiet); } catch (...) { diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index f66c2e841..e4de0f185 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -17,28 +17,87 @@ #include +#include + #include + #include #include + #include +#include + #include using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::evmasm; +using namespace solidity::langutil; -static vector s_evmVersions = { - "homestead", - "tangerineWhistle", - "spuriousDragon", - "byzantium", - "constantinople", - "petersburg", - "istanbul" +static vector s_evmVersions = { + EVMVersion::homestead(), + EVMVersion::tangerineWhistle(), + EVMVersion::spuriousDragon(), + EVMVersion::byzantium(), + EVMVersion::constantinople(), + EVMVersion::petersburg(), + EVMVersion::istanbul(), + EVMVersion::berlin() }; +void FuzzerUtil::testCompilerJsonInterface(string const& _input, bool _optimize, bool _quiet) +{ + if (!_quiet) + cout << "Testing compiler " << (_optimize ? "with" : "without") << " optimizer." << endl; + + Json::Value config = Json::objectValue; + config["language"] = "Solidity"; + config["sources"] = Json::objectValue; + config["sources"][""] = Json::objectValue; + config["sources"][""]["content"] = _input; + config["settings"] = Json::objectValue; + config["settings"]["optimizer"] = Json::objectValue; + config["settings"]["optimizer"]["enabled"] = _optimize; + config["settings"]["optimizer"]["runs"] = 200; + config["settings"]["evmVersion"] = "berlin"; + + // Enable all SourceUnit-level outputs. + config["settings"]["outputSelection"]["*"][""][0] = "*"; + // Enable all Contract-level outputs. + config["settings"]["outputSelection"]["*"]["*"][0] = "*"; + + runCompiler(jsonCompactPrint(config), _quiet); +} + +void FuzzerUtil::testCompiler(string const& _input, bool _optimize) +{ + frontend::CompilerStack compiler; + EVMVersion evmVersion = s_evmVersions[_input.size() % s_evmVersions.size()]; + frontend::OptimiserSettings optimiserSettings; + if (_optimize) + optimiserSettings = frontend::OptimiserSettings::standard(); + else + optimiserSettings = frontend::OptimiserSettings::minimal(); + compiler.setSources({{"", _input}}); + compiler.setEVMVersion(evmVersion); + compiler.setOptimiserSettings(optimiserSettings); + try + { + compiler.compile(); + } + catch (Error const&) + { + } + catch (FatalError const&) + { + } + catch (UnimplementedFeatureError const&) + { + } +} + void FuzzerUtil::runCompiler(string const& _input, bool _quiet) { if (!_quiet) @@ -73,30 +132,6 @@ void FuzzerUtil::runCompiler(string const& _input, bool _quiet) } } -void FuzzerUtil::testCompiler(string const& _input, bool _optimize, bool _quiet) -{ - if (!_quiet) - cout << "Testing compiler " << (_optimize ? "with" : "without") << " optimizer." << endl; - - Json::Value config = Json::objectValue; - config["language"] = "Solidity"; - config["sources"] = Json::objectValue; - config["sources"][""] = Json::objectValue; - config["sources"][""]["content"] = _input; - config["settings"] = Json::objectValue; - config["settings"]["optimizer"] = Json::objectValue; - config["settings"]["optimizer"]["enabled"] = _optimize; - config["settings"]["optimizer"]["runs"] = 200; - config["settings"]["evmVersion"] = s_evmVersions[_input.size() % s_evmVersions.size()]; - - // Enable all SourceUnit-level outputs. - config["settings"]["outputSelection"]["*"][""][0] = "*"; - // Enable all Contract-level outputs. - config["settings"]["outputSelection"]["*"]["*"][0] = "*"; - - runCompiler(jsonCompactPrint(config), _quiet); -} - void FuzzerUtil::testConstantOptimizer(string const& _input, bool _quiet) { if (!_quiet) diff --git a/test/tools/fuzzer_common.h b/test/tools/fuzzer_common.h index edf196c1c..801c8c1c1 100644 --- a/test/tools/fuzzer_common.h +++ b/test/tools/fuzzer_common.h @@ -24,7 +24,8 @@ struct FuzzerUtil { static void runCompiler(std::string const& _input, bool _quiet); - static void testCompiler(std::string const& _input, bool _optimize, bool _quiet); + static void testCompilerJsonInterface(std::string const& _input, bool _optimize, bool _quiet); static void testConstantOptimizer(std::string const& _input, bool _quiet); static void testStandardCompiler(std::string const& _input, bool _quiet); + static void testCompiler(std::string const& _input, bool _optimize); }; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index e439aba64..ad689ffcf 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -161,7 +161,6 @@ TestTool::Result TestTool::process() (AnsiColorized(cout, formatted, {BOLD}) << m_name << ": ").flush(); m_test = m_testCaseCreator(TestCase::Config{m_path.string(), m_options.evmVersion()}); - m_test->validateSettings(); if (m_test->shouldRun()) switch (TestCase::TestResult result = m_test->run(outputMessages, " ", formatted)) { @@ -173,7 +172,7 @@ TestTool::Result TestTool::process() AnsiColorized(cout, formatted, {BOLD, CYAN}) << " Contract:" << endl; m_test->printSource(cout, " ", formatted); - m_test->printUpdatedSettings(cout, " ", formatted); + m_test->printSettings(cout, " ", formatted); cout << endl << outputMessages.str() << endl; return result == TestCase::TestResult::FatalError ? Result::Exception : Result::Failure; @@ -232,7 +231,7 @@ TestTool::Request TestTool::handleResponse(bool _exception) cout << endl; ofstream file(m_path.string(), ios::trunc); m_test->printSource(file); - m_test->printUpdatedSettings(file); + m_test->printSettings(file); file << "// ----" << endl; m_test->printUpdatedExpectations(file, "// "); return Request::Rerun; diff --git a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp index 3a2ac5f27..efed3769f 100644 --- a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp @@ -24,7 +24,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size <= 600) { string input(reinterpret_cast(_data), _size); - FuzzerUtil::testCompiler(input, /*optimize=*/false, /*quiet=*/true); + FuzzerUtil::testCompiler(input, /*optimize=*/false); } return 0; } diff --git a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp index 72a59cba4..fc431ffe5 100644 --- a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp @@ -24,7 +24,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size <= 600) { string input(reinterpret_cast(_data), _size); - FuzzerUtil::testCompiler(input, /*optimize=*/true, /*quiet=*/true); + FuzzerUtil::testCompiler(input, /*optimize=*/true); } return 0; } diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp new file mode 100644 index 000000000..8dd6e7564 --- /dev/null +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -0,0 +1,525 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +using namespace std; +using namespace boost::unit_test::framework; +using namespace boost::test_tools; +using namespace solidity::langutil; +using namespace solidity::util; + +namespace fs = boost::filesystem; + +namespace solidity::phaser::test +{ + +class CountingAlgorithm: public GeneticAlgorithm +{ +public: + using GeneticAlgorithm::GeneticAlgorithm; + Population runNextRound(Population _population) override + { + ++m_currentRound; + return _population; + } + + size_t m_currentRound = 0; +}; + +class RandomisingAlgorithm: public GeneticAlgorithm +{ +public: + using GeneticAlgorithm::GeneticAlgorithm; + Population runNextRound(Population _population) override + { + return Population::makeRandom(_population.fitnessMetric(), _population.individuals().size(), 10, 20); + } +}; + +class AlgorithmRunnerFixture +{ +protected: + // NOTE: Regexes here should not contain spaces because we strip them before matching + regex RoundSummaryRegex{R"(-+ROUND\d+\[round:[0-9.]+s,total:[0-9.]+s\]-+)"}; + regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"}; + + string individualPattern(Individual const& individual) const + { + ostringstream output; + output << individual.fitness << individual.chromosome; + return output.str(); + } + + string topChromosomePattern(size_t roundNumber, Individual const& individual) const + { + ostringstream output; + output << roundNumber << R"(\|[0-9.]+\|)" << individualPattern(individual); + return output.str(); + } + + bool nextLineMatches(stringstream& stream, regex const& pattern) const + { + string line; + if (getline(stream, line).fail()) + return false; + + return regex_match(stripWhitespace(line), pattern); + } + + shared_ptr m_fitnessMetric = make_shared(); + Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20); + stringstream m_output; + AlgorithmRunner::Options m_options; +}; + +class AlgorithmRunnerAutosaveFixture: public AlgorithmRunnerFixture +{ +public: + static vector chromosomeStrings(Population const& _population) + { + vector lines; + for (auto const& individual: _population.individuals()) + lines.push_back(toString(individual.chromosome)); + + return lines; + } + +protected: + TemporaryDirectory m_tempDir; + string const m_autosavePath = m_tempDir.memberPath("population-autosave.txt"); + RandomisingAlgorithm m_algorithm; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest) + +BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 5; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + + CountingAlgorithm algorithm; + + BOOST_TEST(algorithm.m_currentRound == 0); + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 5); + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 10); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = true; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_summary_if_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = false; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(""))); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = true; + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_only_top_chromosome_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + m_options.showRoundInfo = true; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(topChromosomePattern(1, runner.population().individuals()[0])))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_number_for_top_chromosome_if_round_info_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + m_options.showRoundInfo = false; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(runner.population().individuals()[0])))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty_and_only_top_chromosome_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 3; + m_options.showRoundInfo = true; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_initial_population_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = true; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = false; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); + for (auto const& individual: m_population.individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_initial_population_if_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = false; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_whole_initial_population_even_if_only_top_chromosome_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = true; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); + for (auto const& individual: m_population.individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 4; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + vector sourceStreams = { + CharStream("{mstore(10, 20)}", ""), + CharStream("{mstore(10, 20)\nsstore(10, 20)}", ""), + }; + vector programs = { + get(Program::load(sourceStreams[0])), + get(Program::load(sourceStreams[1])), + }; + vector> caches = { + make_shared(programs[0]), + make_shared(programs[1]), + }; + shared_ptr fitnessMetric = make_shared(vector>{ + make_shared(nullopt, caches[0]), + make_shared(nullopt, caches[1]), + }); + Population population = Population::makeRandom(fitnessMetric, 2, 0, 5); + + AlgorithmRunner runner(population, caches, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(caches[0]->currentRound() == m_options.maxRounds.value()); + BOOST_TEST(caches[1]->currentRound() == m_options.maxRounds.value()); + + CacheStats stats = caches[0]->gatherStats() + caches[1]->gatherStats(); + + for (size_t i = 0; i < m_options.maxRounds.value() - 1; ++i) + { + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + if (i > 0) + BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + } + + BOOST_REQUIRE(stats.roundEntryCounts.size() == 2); + BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value() - 1) == 1); + BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value()) == 1); + + size_t round = m_options.maxRounds.value(); + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round - 1) + ":" + toString(stats.roundEntryCounts[round - 1]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round) + ":" + toString(stats.roundEntryCounts[round]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, regex("Totalhits:" + toString(stats.hits)))); + BOOST_TEST(nextLineMatches(m_output, regex("Totalmisses:" + toString(stats.misses)))); + BOOST_TEST(nextLineMatches(m_output, regex("Sizeofcachedcode:" + toString(stats.totalCodeSize)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_message_if_cache_stats_requested_but_cache_disabled, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {nullptr}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled")))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_partial_stats_and_message_if_some_caches_disabled, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + CharStream sourceStream = CharStream("{}", ""); + shared_ptr cache = make_shared(get(Program::load(sourceStream))); + + AlgorithmRunner runner(m_population, {cache, nullptr}, m_options, m_output); + BOOST_REQUIRE(cache->gatherStats().roundEntryCounts.size() == 0); + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled for 1 out of 2 programs")))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 0; + m_options.populationAutosaveFile = m_autosavePath; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + runner.run(m_algorithm); + assert(runner.population() == m_population); + + BOOST_TEST(fs::is_regular_file(m_autosavePath)); + BOOST_TEST(readLinesFromFile(m_autosavePath) == chromosomeStrings(runner.population())); +} + +BOOST_FIXTURE_TEST_CASE(run_should_save_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 1; + m_options.populationAutosaveFile = m_autosavePath; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + runner.run(m_algorithm); + assert(runner.population() != m_population); + + BOOST_TEST(fs::is_regular_file(m_autosavePath)); + BOOST_TEST(readLinesFromFile(m_autosavePath) == chromosomeStrings(runner.population())); +} + +BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 5; + m_options.populationAutosaveFile = m_autosavePath; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + vector originalContent = {"Original content"}; + { + ofstream tmpFile(m_autosavePath); + tmpFile << originalContent[0] << endl; + } + assert(fs::exists(m_autosavePath)); + assert(readLinesFromFile(m_autosavePath) == originalContent); + + runner.run(m_algorithm); + + BOOST_TEST(fs::is_regular_file(m_autosavePath)); + BOOST_TEST(readLinesFromFile(m_autosavePath) != originalContent); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_not_specified, AlgorithmRunnerAutosaveFixture) +{ + m_options.maxRounds = 5; + m_options.populationAutosaveFile = nullopt; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + assert(!fs::exists(m_autosavePath)); + + runner.run(m_algorithm); + + BOOST_TEST(!fs::exists(m_autosavePath)); +} + +BOOST_FIXTURE_TEST_CASE(run_should_randomise_duplicate_chromosomes_if_requested, AlgorithmRunnerFixture) +{ + Chromosome duplicate("afc"); + Population population(m_fitnessMetric, {duplicate, duplicate, duplicate}); + CountingAlgorithm algorithm; + + m_options.maxRounds = 1; + m_options.randomiseDuplicates = true; + m_options.minChromosomeLength = 50; + m_options.maxChromosomeLength = 50; + AlgorithmRunner runner(population, {}, m_options, m_output); + + runner.run(algorithm); + + auto const& newIndividuals = runner.population().individuals(); + + BOOST_TEST(newIndividuals.size() == 3); + BOOST_TEST(( + newIndividuals[0].chromosome == duplicate || + newIndividuals[1].chromosome == duplicate || + newIndividuals[2].chromosome == duplicate + )); + BOOST_TEST(newIndividuals[0] != newIndividuals[1]); + BOOST_TEST(newIndividuals[0] != newIndividuals[2]); + BOOST_TEST(newIndividuals[1] != newIndividuals[2]); + + BOOST_TEST((newIndividuals[0].chromosome.length() == 50 || newIndividuals[0].chromosome == duplicate)); + BOOST_TEST((newIndividuals[1].chromosome.length() == 50 || newIndividuals[1].chromosome == duplicate)); + BOOST_TEST((newIndividuals[2].chromosome.length() == 50 || newIndividuals[2].chromosome == duplicate)); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_requested, AlgorithmRunnerFixture) +{ + Chromosome duplicate("afc"); + Population population(m_fitnessMetric, {duplicate, duplicate, duplicate}); + CountingAlgorithm algorithm; + + m_options.maxRounds = 1; + m_options.randomiseDuplicates = false; + AlgorithmRunner runner(population, {}, m_options, m_output); + + runner.run(algorithm); + + BOOST_TEST(runner.population().individuals().size() == 3); + BOOST_TEST(runner.population().individuals()[0].chromosome == duplicate); + BOOST_TEST(runner.population().individuals()[1].chromosome == duplicate); + BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate); +} + +BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_before_each_round, AlgorithmRunnerFixture) +{ + CharStream sourceStream = CharStream("{}", current_test_case().p_name); + vector> caches = { + make_shared(get(Program::load(sourceStream))), + make_shared(get(Program::load(sourceStream))), + }; + + m_options.maxRounds = 10; + AlgorithmRunner runner(m_population, caches, m_options, m_output); + CountingAlgorithm algorithm; + + BOOST_TEST(algorithm.m_currentRound == 0); + BOOST_TEST(caches[0]->currentRound() == 0); + BOOST_TEST(caches[1]->currentRound() == 0); + + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 10); + BOOST_TEST(caches[0]->currentRound() == 10); + BOOST_TEST(caches[1]->currentRound() == 10); + + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 20); + BOOST_TEST(caches[0]->currentRound() == 10); + BOOST_TEST(caches[1]->currentRound() == 10); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index 23de34a2d..442f7967f 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include diff --git a/test/yulPhaser/Common.cpp b/test/yulPhaser/Common.cpp index 93aa432fa..39e6dd784 100644 --- a/test/yulPhaser/Common.cpp +++ b/test/yulPhaser/Common.cpp @@ -15,52 +15,135 @@ along with solidity. If not, see . */ -#include +#include -#include +#include -#include +#include + +#include +#include + +#include +#include +#include using namespace std; -using namespace solidity; -using namespace solidity::yul; +using namespace boost::test_tools; +using namespace solidity::util; -vector phaser::test::chromosomeLengths(Population const& _population) +namespace solidity::phaser::test { - vector lengths; - for (auto const& individual: _population.individuals()) - lengths.push_back(individual.chromosome.length()); - return lengths; +class ReadLinesFromFileFixture +{ +protected: + TemporaryDirectory m_tempDir; +}; + +namespace +{ + +enum class TestEnum +{ + A, + B, + AB, + CD, + EF, + GH, +}; + +map const TestEnumToStringMap = +{ + {TestEnum::A, "a"}, + {TestEnum::B, "b"}, + {TestEnum::AB, "a b"}, + {TestEnum::CD, "c-d"}, + {TestEnum::EF, "e f"}, +}; +map const StringToTestEnumMap = invertMap(TestEnumToStringMap); + } -map phaser::test::enumerateOptmisationSteps() +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(CommonTest) + +BOOST_FIXTURE_TEST_CASE(readLinesFromFile_should_return_all_lines_from_a_text_file_as_strings_without_newlines, ReadLinesFromFileFixture) { - map stepIndices; - size_t i = 0; - for (auto const& nameAndAbbreviation: OptimiserSuite::stepNameToAbbreviationMap()) - stepIndices.insert({nameAndAbbreviation.first, i++}); - - return stepIndices; -} - -string phaser::test::stripWhitespace(string const& input) -{ - regex whitespaceRegex("\\s+"); - return regex_replace(input, whitespaceRegex, ""); -} - -size_t phaser::test::countSubstringOccurrences(string const& _inputString, string const& _substring) -{ - assert(_substring.size() > 0); - - size_t count = 0; - size_t lastOccurrence = 0; - while ((lastOccurrence = _inputString.find(_substring, lastOccurrence)) != string::npos) { - ++count; - lastOccurrence += _substring.size(); + ofstream tmpFile(m_tempDir.memberPath("test-file.txt")); + tmpFile << endl << "Line 1" << endl << endl << endl << "Line 2" << endl << "#" << endl << endl; } - return count; + vector lines = readLinesFromFile(m_tempDir.memberPath("test-file.txt")); + BOOST_TEST((lines == vector{"", "Line 1", "", "", "Line 2", "#", ""})); +} + +BOOST_AUTO_TEST_CASE(deserializeChoice_should_convert_string_to_enum) +{ + istringstream aStream("a"); + TestEnum aResult; + deserializeChoice(aStream, aResult, StringToTestEnumMap); + BOOST_CHECK(aResult == TestEnum::A); + BOOST_TEST(!aStream.fail()); + + istringstream bStream("b"); + TestEnum bResult; + deserializeChoice(bStream, bResult, StringToTestEnumMap); + BOOST_CHECK(bResult == TestEnum::B); + BOOST_TEST(!bStream.fail()); + + istringstream cdStream("c-d"); + TestEnum cdResult; + deserializeChoice(cdStream, cdResult, StringToTestEnumMap); + BOOST_CHECK(cdResult == TestEnum::CD); + BOOST_TEST(!cdStream.fail()); +} + +BOOST_AUTO_TEST_CASE(deserializeChoice_should_set_failbit_if_there_is_no_enum_corresponding_to_string) +{ + istringstream xyzStream("xyz"); + TestEnum xyzResult; + deserializeChoice(xyzStream, xyzResult, StringToTestEnumMap); + BOOST_TEST(xyzStream.fail()); +} + +BOOST_AUTO_TEST_CASE(deserializeChoice_does_not_have_to_support_strings_with_spaces) +{ + istringstream abStream("a b"); + TestEnum abResult; + deserializeChoice(abStream, abResult, StringToTestEnumMap); + BOOST_CHECK(abResult == TestEnum::A); + BOOST_TEST(!abStream.fail()); + + istringstream efStream("e f"); + TestEnum efResult; + deserializeChoice(efStream, efResult, StringToTestEnumMap); + BOOST_TEST(efStream.fail()); +} + +BOOST_AUTO_TEST_CASE(serializeChoice_should_convert_enum_to_string) +{ + output_test_stream output; + + serializeChoice(output, TestEnum::A, TestEnumToStringMap); + BOOST_CHECK(output.is_equal("a")); + BOOST_TEST(!output.fail()); + + serializeChoice(output, TestEnum::AB, TestEnumToStringMap); + BOOST_CHECK(output.is_equal("a b")); + BOOST_TEST(!output.fail()); +} + +BOOST_AUTO_TEST_CASE(serializeChoice_should_set_failbit_if_there_is_no_string_corresponding_to_enum) +{ + output_test_stream output; + serializeChoice(output, TestEnum::GH, TestEnumToStringMap); + BOOST_TEST(output.fail()); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + } diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 58561806d..524ef6357 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -22,22 +22,30 @@ #include +#include + #include +#include + using namespace std; using namespace solidity::langutil; +using namespace solidity::util; using namespace solidity::yul; namespace solidity::phaser::test { -class FitnessMetricFixture +class DummyProgramBasedMetric: public ProgramBasedMetric +{ +public: + using ProgramBasedMetric::ProgramBasedMetric; + size_t evaluate(Chromosome const&) override { return 0; } +}; + +class ProgramBasedMetricFixture { protected: - FitnessMetricFixture(): - m_sourceStream(SampleSourceCode, ""), - m_program(Program::load(m_sourceStream)) {} - static constexpr char SampleSourceCode[] = "{\n" " function foo() -> result\n" @@ -52,57 +60,213 @@ protected: " mstore(foo(), bar())\n" "}\n"; - CharStream m_sourceStream; - Program m_program; + Program optimisedProgram(Program _program) const + { + [[maybe_unused]] size_t originalSize = _program.codeSize(); + Program result = move(_program); + result.optimise(m_chromosome.optimisationSteps()); + + // Make sure that the program and the chromosome we have chosen are suitable for the test + assert(result.codeSize() != originalSize); + + return result; + } + + CharStream m_sourceStream = CharStream(SampleSourceCode, ""); + Chromosome m_chromosome{vector{UnusedPruner::name, EquivalentFunctionCombiner::name}}; + Program m_program = get(Program::load(m_sourceStream)); + Program m_optimisedProgram = optimisedProgram(m_program); + shared_ptr m_programCache = make_shared(m_program); +}; + +class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture +{ +protected: + vector> m_simpleMetrics = { + make_shared(m_program, nullptr, 1), + make_shared(m_program, nullptr, 2), + make_shared(m_program, nullptr, 3), + }; + vector m_fitness = { + m_simpleMetrics[0]->evaluate(m_chromosome), + m_simpleMetrics[1]->evaluate(m_chromosome), + m_simpleMetrics[2]->evaluate(m_chromosome), + }; }; BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(FitnessMetricsTest) +BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest) + +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgram(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgram(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgramNoCache(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgramNoCache(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); + BOOST_TEST(m_programCache->size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ProgramSizeTest) -BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + size_t fitness = ProgramSize(m_program, nullptr).evaluate(m_chromosome); - Program optimisedProgram = m_program; - optimisedProgram.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != optimisedProgram.codeSize()); - - BOOST_TEST(ProgramSize(m_program).evaluate(chromosome) != m_program.codeSize()); - BOOST_TEST(ProgramSize(m_program).evaluate(chromosome) == optimisedProgram.codeSize()); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness == m_optimisedProgram.codeSize()); } -BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + size_t fitness = ProgramSize(nullopt, m_programCache).evaluate(m_chromosome); - Program programOptimisedOnce = m_program; - programOptimisedOnce.optimise(chromosome.optimisationSteps()); - Program programOptimisedTwice = programOptimisedOnce; - programOptimisedTwice.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != programOptimisedOnce.codeSize()); - assert(m_program.codeSize() != programOptimisedTwice.codeSize()); - assert(programOptimisedOnce.codeSize() != programOptimisedTwice.codeSize()); - - ProgramSize metric(m_program, 2); - - BOOST_TEST(metric.evaluate(chromosome) != m_program.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) != programOptimisedOnce.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) == programOptimisedTwice.codeSize()); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness == m_optimisedProgram.codeSize()); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); } -BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, FitnessMetricFixture) +BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) { - Chromosome chromosome(vector{UnusedPruner::name, EquivalentFunctionCombiner::name}); + Program const& programOptimisedOnce = m_optimisedProgram; + Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); - Program optimisedProgram = m_program; - optimisedProgram.optimise(chromosome.optimisationSteps()); - assert(m_program.codeSize() != optimisedProgram.codeSize()); + ProgramSize metric(m_program, nullptr, 2); + size_t fitness = metric.evaluate(m_chromosome); - ProgramSize metric(m_program, 0); + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness != programOptimisedOnce.codeSize()); + BOOST_TEST(fitness == programOptimisedTwice.codeSize()); +} - BOOST_TEST(metric.evaluate(chromosome) == m_program.codeSize()); - BOOST_TEST(metric.evaluate(chromosome) != optimisedProgram.codeSize()); +BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) +{ + ProgramSize metric(m_program, nullptr, 0); + size_t fitness = metric.evaluate(m_chromosome); + + BOOST_TEST(fitness == m_program.codeSize()); + BOOST_TEST(fitness != m_optimisedProgram.codeSize()); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(RelativeProgramSizeTest) + +BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture) +{ + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) +{ + BOOST_TEST(RelativeProgramSize(nullopt, m_programCache, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) +{ + Program const& programOptimisedOnce = m_optimisedProgram; + Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); + + RelativeProgramSize metric(m_program, nullptr, 3, 2); + size_t fitness = metric.evaluate(m_chromosome); + + BOOST_TEST(fitness != 1000); + BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, 1).evaluate(m_chromosome)); + BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) +{ + RelativeProgramSize metric(m_program, nullptr, 3, 0); + + BOOST_TEST(metric.evaluate(m_chromosome) == 1000); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_is_zero, ProgramBasedMetricFixture) +{ + CharStream sourceStream = CharStream("{}", ""); + Program program = get(Program::load(sourceStream)); + + RelativeProgramSize metric(program, nullptr, 3); + + BOOST_TEST(metric.evaluate(m_chromosome) == 1000); + BOOST_TEST(metric.evaluate(Chromosome("")) == 1000); + BOOST_TEST(metric.evaluate(Chromosome("afcxjLTLTDoO")) == 1000); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture) +{ + double sizeRatio = static_cast(m_optimisedProgram.codeSize()) / m_program.codeSize(); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(FitnessMetricCombinationTest) + +BOOST_FIXTURE_TEST_CASE(FitnessMetricAverage_evaluate_should_compute_average_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricAverage metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == (m_fitness[0] + m_fitness[1] + m_fitness[2]) / 3); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricSum_evaluate_should_compute_sum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricSum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == m_fitness[0] + m_fitness[1] + m_fitness[2]); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricMaximum_evaluate_should_compute_maximum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricMaximum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == max(m_fitness[0], max(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.metrics() == m_simpleMetrics); +} + +BOOST_FIXTURE_TEST_CASE(FitnessMetricMinimum_evaluate_should_compute_minimum_of_values_returned_by_metrics_passed_to_it, FitnessMetricCombinationFixture) +{ + FitnessMetricMinimum metric(m_simpleMetrics); + + assert(m_simpleMetrics.size() == 3); + BOOST_TEST(metric.evaluate(m_chromosome) == min(m_fitness[0], min(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.metrics() == m_simpleMetrics); } BOOST_AUTO_TEST_SUITE_END() @@ -110,4 +274,3 @@ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() } - diff --git a/test/yulPhaser/GeneticAlgorithms.cpp b/test/yulPhaser/GeneticAlgorithms.cpp index aaa0a0b05..f7a5e7a92 100644 --- a/test/yulPhaser/GeneticAlgorithms.cpp +++ b/test/yulPhaser/GeneticAlgorithms.cpp @@ -15,121 +15,175 @@ along with solidity. If not, see . */ -#include +#include #include #include #include -#include - -#include #include #include -#include +#include #include using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; -using namespace solidity::langutil; -using namespace solidity::util; namespace solidity::phaser::test { -class DummyAlgorithm: public GeneticAlgorithm -{ -public: - using GeneticAlgorithm::GeneticAlgorithm; - void runNextRound() override { ++m_currentRound; } - - size_t m_currentRound = 0; -}; - class GeneticAlgorithmFixture { protected: shared_ptr m_fitnessMetric = make_shared(); - output_test_stream m_output; }; BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest) -BOOST_AUTO_TEST_SUITE(GeneticAlgorithmTest) - -BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, GeneticAlgorithmFixture) -{ - DummyAlgorithm algorithm(Population(m_fitnessMetric), m_output); - - BOOST_TEST(algorithm.m_currentRound == 0); - algorithm.run(10); - BOOST_TEST(algorithm.m_currentRound == 10); - algorithm.run(3); - BOOST_TEST(algorithm.m_currentRound == 13); -} - -BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, GeneticAlgorithmFixture) -{ - // run() is allowed to print more but should at least print the first one - - DummyAlgorithm algorithm( - // NOTE: Chromosomes chosen so that they're not substrings of each other and are not - // words likely to appear in the output in normal circumstances. - Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), - m_output - ); - - BOOST_TEST(m_output.is_empty()); - algorithm.run(1); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 1); - algorithm.run(3); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(algorithm.population().individuals()[0].chromosome)) == 4); -} - -BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest) BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_randomise_rest_of_population, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.5, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.5, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{1, 1, 1, 1, 3, 3, 3, 3})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 3, 3, 3, 3})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.5, 7, 7}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.5, 7, 7}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 7, 7, 7, 7})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 7, 7, 7, 7})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_replace_all_chromosomes_if_zero_size_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {0.0, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({0.0, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{1, 1, 1, 1, 1, 1, 1, 1})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 1, 1, 1, 1})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_any_chromosomes_if_whole_population_is_the_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - RandomAlgorithm algorithm(population, m_output, {1.0, 1, 1}); - assert((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + RandomAlgorithm algorithm({1.0, 1, 1}); - algorithm.runNextRound(); - BOOST_TEST((chromosomeLengths(algorithm.population()) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + Population newPopulation = algorithm.runNextRound(population); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 5, 5, 5, 5})); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(GenerationalElitistWithExclusivePoolsTest) + +BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_of_population, GeneticAlgorithmFixture) +{ + auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.2, + /* crossoverPoolSize = */ 0.2, + /* randomisationChance = */ 0.0, + /* deletionVsAdditionChance = */ 1.0, + /* percentGenesToRandomise = */ 0.0, + /* percentGenesToAddOrDelete = */ 1.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + Population newPopulation = algorithm.runNextRound(population); + + BOOST_TEST((chromosomeLengths(newPopulation) == vector{0, 0, 3, 3, 3, 3, 3, 3, 3, 3})); +} + +BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) +{ + auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); + assert(chromosomeLengths(population) == (vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.2, + /* crossoverPoolSize = */ 0.2, + /* randomisationChance = */ 0.0, + /* deletionVsAdditionChance = */ 0.0, + /* percentGenesToRandomise = */ 0.0, + /* percentGenesToAddOrDelete = */ 1.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + Population newPopulation = algorithm.runNextRound(population); + + BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 3, 3, 3, 3, 7, 7})); +} + +BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossover_pool_by_mutating_the_elite, GeneticAlgorithmFixture) +{ + auto population = Population::makeRandom(m_fitnessMetric, 20, 5, 5); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.8, + /* crossoverPoolSize = */ 0.0, + /* randomisationChance = */ 0.5, + /* deletionVsAdditionChance = */ 0.5, + /* percentGenesToRandomise = */ 1.0, + /* percentGenesToAddOrDelete = */ 1.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + SimulationRNG::reset(1); + Population newPopulation = algorithm.runNextRound(population); + + BOOST_TEST(( + chromosomeLengths(newPopulation) == + vector{0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 11} + )); +} + +BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossover_pool_by_crossing_over_the_elite, GeneticAlgorithmFixture) +{ + auto population = ( + Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("ff")}) + + Population::makeRandom(m_fitnessMetric, 8, 6, 6) + ); + assert((chromosomeLengths(population) == vector{2, 2, 6, 6, 6, 6, 6, 6, 6, 6})); + + GenerationalElitistWithExclusivePools::Options options = { + /* mutationPoolSize = */ 0.0, + /* crossoverPoolSize = */ 0.8, + /* randomisationChance = */ 0.0, + /* deletionVsAdditionChance = */ 0.0, + /* percentGenesToRandomise = */ 0.0, + /* percentGenesToAddOrDelete = */ 0.0, + }; + GenerationalElitistWithExclusivePools algorithm(options); + + SimulationRNG::reset(1); + Population newPopulation = algorithm.runNextRound(population); + + vector const& newIndividuals = newPopulation.individuals(); + BOOST_TEST((chromosomeLengths(newPopulation) == vector{2, 2, 2, 2, 2, 2, 2, 2, 2, 2})); + for (auto& individual: newIndividuals) + BOOST_TEST(( + individual.chromosome == Chromosome("aa") || + individual.chromosome == Chromosome("af") || + individual.chromosome == Chromosome("fa") || + individual.chromosome == Chromosome("ff") + )); + BOOST_TEST(any_of(newIndividuals.begin() + 2, newIndividuals.end(), [](auto& individual){ + return individual.chromosome != Chromosome("aa") && individual.chromosome != Chromosome("ff"); + })); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/Mutations.cpp b/test/yulPhaser/Mutations.cpp new file mode 100644 index 000000000..df58cec54 --- /dev/null +++ b/test/yulPhaser/Mutations.cpp @@ -0,0 +1,394 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include + +#include +#include + +using namespace std; + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(MutationsTest) +BOOST_AUTO_TEST_SUITE(GeneRandomisationTest) + +BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_them_with_random_ones_with_given_probability) +{ + Chromosome chromosome("fcCUnDvejs"); + function mutation01 = geneRandomisation(0.1); + function mutation05 = geneRandomisation(0.5); + function mutation10 = geneRandomisation(1.0); + + SimulationRNG::reset(1); + BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 2); + BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 5); + BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 7); + SimulationRNG::reset(2); + BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 1); + BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 3); + BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 9); +} + +BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_probability_is_zero) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneRandomisation(0.0); + + BOOST_TEST(mutation(chromosome) == chromosome); +} + +BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with_given_probability) +{ + Chromosome chromosome("fcCUnDvejs"); + function mutation01 = geneDeletion(0.1); + function mutation05 = geneDeletion(0.5); + + SimulationRNG::reset(1); + // fcCUnDvejs + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcCU Dvejs"))); + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" D ejs"))); + SimulationRNG::reset(2); + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcUnDvejs"))); + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" Un s"))); +} + +BOOST_AUTO_TEST_CASE(geneDeletion_should_return_identical_chromosome_if_probability_is_zero) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneDeletion(0.0); + + BOOST_TEST(mutation(chromosome) == chromosome); +} + +BOOST_AUTO_TEST_CASE(geneDeletion_should_delete_all_genes_if_probability_is_one) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneDeletion(1.0); + + BOOST_TEST(mutation(chromosome) == Chromosome("")); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_new_genes_with_given_probability) +{ + Chromosome chromosome("fcCUnDvejs"); + function mutation01 = geneAddition(0.1); + function mutation05 = geneAddition(0.5); + + SimulationRNG::reset(1); + // f c C U n D v e j s + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f c C UC n D v e jx s"))); // 20% more + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("j f cu C U ne D v eI j sf"))); // 50% more + SimulationRNG::reset(2); + BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f cu C U n D v e j s"))); // 10% more + BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("L f ce Cv U n D v e jO s"))); // 40% more +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position) +{ + SimulationRNG::reset(7); + Chromosome chromosome("fcCUnDvejs"); + function mutation = geneAddition(0.1); + + Chromosome mutatedChromosome = mutation(chromosome); + BOOST_TEST(mutatedChromosome.length() > chromosome.length()); + + vector suffix( + mutatedChromosome.optimisationSteps().end() - chromosome.length(), + mutatedChromosome.optimisationSteps().end() + ); + BOOST_TEST(suffix == chromosome.optimisationSteps()); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) +{ + SimulationRNG::reset(81); + Chromosome chromosome("fcCUnDvejs"); + function mutation = geneAddition(0.1); + + Chromosome mutatedChromosome = mutation(chromosome); + BOOST_TEST(mutatedChromosome.length() > chromosome.length()); + + vector prefix( + mutatedChromosome.optimisationSteps().begin(), + mutatedChromosome.optimisationSteps().begin() + chromosome.length() + ); + BOOST_TEST(prefix == chromosome.optimisationSteps()); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_return_identical_chromosome_if_probability_is_zero) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneAddition(0.0); + + BOOST_TEST(mutation(chromosome) == chromosome); +} + +BOOST_AUTO_TEST_CASE(geneAddition_should_insert_genes_at_all_positions_if_probability_is_one) +{ + Chromosome chromosome("fcCUnDvejsrmV"); + function mutation = geneAddition(1.0); + + Chromosome mutatedChromosome = mutation(chromosome); + BOOST_TEST(mutatedChromosome.length() == chromosome.length() * 2 + 1); + + vector originalGenes; + for (size_t i = 0; i < mutatedChromosome.length() - 1; ++i) + if (i % 2 == 1) + originalGenes.push_back(mutatedChromosome.optimisationSteps()[i]); + + BOOST_TEST(Chromosome(originalGenes) == chromosome); +} + +BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_given_probability) +{ + SimulationRNG::reset(1); + Chromosome chromosome("a"); + function mutation = alternativeMutations( + 0.8, + wholeChromosomeReplacement(Chromosome("c")), + wholeChromosomeReplacement(Chromosome("f")) + ); + + size_t cCount = 0; + size_t fCount = 0; + for (size_t i = 0; i < 10; ++i) + { + Chromosome mutatedChromosome = mutation(chromosome); + cCount += static_cast(mutatedChromosome == Chromosome("c")); + fCount += static_cast(mutatedChromosome == Chromosome("f")); + } + + // This particular seed results in 7 "c"s out of 10 which looks plausible given the 80% chance. + BOOST_TEST(cCount == 7); + BOOST_TEST(fCount == 3); +} + +BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_first_mutation_if_probability_is_one) +{ + Chromosome chromosome("a"); + function mutation = alternativeMutations( + 1.0, + wholeChromosomeReplacement(Chromosome("c")), + wholeChromosomeReplacement(Chromosome("f")) + ); + + for (size_t i = 0; i < 10; ++i) + BOOST_TEST(mutation(chromosome) == Chromosome("c")); +} + +BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_if_probability_is_zero) +{ + Chromosome chromosome("a"); + function mutation = alternativeMutations( + 0.0, + wholeChromosomeReplacement(Chromosome("c")), + wholeChromosomeReplacement(Chromosome("f")) + ); + + for (size_t i = 0; i < 10; ++i) + BOOST_TEST(mutation(chromosome) == Chromosome("f")); +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point) +{ + function crossover = randomPointCrossover(); + + SimulationRNG::reset(1); + Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); + BOOST_TEST(result1 == Chromosome("aaaccc")); + + SimulationRNG::reset(1); + Chromosome result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); + BOOST_TEST(result2 == Chromosome("cccaaaaaaa")); +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_on_both_chromosomes) +{ + SimulationRNG::reset(1); + function crossover = randomPointCrossover(); + + for (size_t i = 0; i < 30; ++i) + { + Chromosome result1 = crossover(Chromosome("aaa"), Chromosome("TTTTTTTTTTTTTTTTTTTT")); + Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aaa")); + BOOST_TEST(( + result1 == Chromosome("TTTTTTTTTTTTTTTTTTTT") || + result1 == Chromosome("aTTTTTTTTTTTTTTTTTTT") || + result1 == Chromosome("aaTTTTTTTTTTTTTTTTTT") || + result1 == Chromosome("aaaTTTTTTTTTTTTTTTTT") + )); + BOOST_TEST(( + result2 == Chromosome("aaa") || + result2 == Chromosome("Taa") || + result2 == Chromosome("TTa") || + result2 == Chromosome("TTT") + )); + } +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_splittable) +{ + SimulationRNG::reset(1); + function crossover = randomPointCrossover(); + + for (size_t i = 0; i < 30; ++i) + { + Chromosome result1 = crossover(Chromosome("aa"), Chromosome("TTTTTTTTTTTTTTTTTTTT")); + Chromosome result2 = crossover(Chromosome("TTTTTTTTTTTTTTTTTTTT"), Chromosome("aa")); + BOOST_TEST(result1 != Chromosome("TTTTTTTTTTTTTTTTTTTT")); + BOOST_TEST(result2 != Chromosome("aa")); + } +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_not_empty) +{ + SimulationRNG::reset(1); + function crossover = randomPointCrossover(); + + for (size_t i = 0; i < 30; ++i) + { + Chromosome result1 = crossover(Chromosome("a"), Chromosome("T")); + Chromosome result2 = crossover(Chromosome("T"), Chromosome("a")); + BOOST_TEST(result1 == Chromosome("a")); + BOOST_TEST(result2 == Chromosome("T")); + } +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_work_even_if_one_chromosome_is_unsplittable) +{ + function crossover = randomPointCrossover(); + + SimulationRNG::reset(1); + BOOST_CHECK(crossover(Chromosome("ff"), Chromosome("a")) == Chromosome("f")); + BOOST_CHECK(crossover(Chromosome("a"), Chromosome("ff")) == Chromosome("af")); +} + +BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_at_least_one_chromosome_is_empty) +{ + Chromosome empty(""); + Chromosome unsplittable("a"); + Chromosome splittable("aaaa"); + function crossover = randomPointCrossover(); + + SimulationRNG::reset(1); + BOOST_CHECK(crossover(empty, empty) == empty); + BOOST_CHECK(crossover(unsplittable, empty) == empty); + BOOST_CHECK(crossover(empty, unsplittable) == unsplittable); + BOOST_CHECK(crossover(splittable, empty) == empty); + BOOST_CHECK(crossover(empty, splittable) == splittable); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_swap_chromosome_parts_at_given_point) +{ + Chromosome result1 = fixedPointCrossover(0.8)(Chromosome("aaaaaaaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(0.8)(Chromosome("cccccccccc"), Chromosome("aaaaaaaaaa")); + BOOST_TEST(result1 == Chromosome("aaaaaaaacc")); + BOOST_TEST(result2 == Chromosome("ccccccccaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_determine_crossover_point_based_on_length_of_shorter_chromosome) +{ + Chromosome result1 = fixedPointCrossover(0.4)(Chromosome("aaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(0.4)(Chromosome("cccccccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("aacccccccc")); + BOOST_TEST(result2 == Chromosome("ccaaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_round_split_point) +{ + Chromosome result1 = fixedPointCrossover(0.49)(Chromosome("aaaaa"), Chromosome("ccccc")); + Chromosome result2 = fixedPointCrossover(0.49)(Chromosome("ccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("aaccc")); + BOOST_TEST(result2 == Chromosome("ccaaa")); + + Chromosome result3 = fixedPointCrossover(0.50)(Chromosome("aaaaa"), Chromosome("ccccc")); + Chromosome result4 = fixedPointCrossover(0.50)(Chromosome("ccccc"), Chromosome("aaaaa")); + BOOST_TEST(result3 == Chromosome("aaacc")); + BOOST_TEST(result4 == Chromosome("cccaa")); + + Chromosome result5 = fixedPointCrossover(0.51)(Chromosome("aaaaa"), Chromosome("ccccc")); + Chromosome result6 = fixedPointCrossover(0.51)(Chromosome("ccccc"), Chromosome("aaaaa")); + BOOST_TEST(result5 == Chromosome("aaacc")); + BOOST_TEST(result6 == Chromosome("cccaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_position_zero_if_explicitly_requested) +{ + Chromosome result1 = fixedPointCrossover(0.0)(Chromosome("aaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(0.0)(Chromosome("cccccccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("cccccccccc")); + BOOST_TEST(result2 == Chromosome("aaaaa")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_end_of_shorter_chromosome_if_crossover_point_is_after_last_position) +{ + Chromosome result1 = fixedPointCrossover(1.0)(Chromosome("aaaaa"), Chromosome("cccccccccc")); + Chromosome result2 = fixedPointCrossover(1.0)(Chromosome("cccccccccc"), Chromosome("aaaaa")); + BOOST_TEST(result1 == Chromosome("aaaaaccccc")); + BOOST_TEST(result2 == Chromosome("ccccc")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_select_correct_split_point_for_unsplittable_chromosomes) +{ + function crossover00 = fixedPointCrossover(0.0); + BOOST_CHECK(crossover00(Chromosome("fff"), Chromosome("a")) == Chromosome("a")); + BOOST_CHECK(crossover00(Chromosome("a"), Chromosome("fff")) == Chromosome("fff")); + + BOOST_CHECK(crossover00(Chromosome("f"), Chromosome("a")) == Chromosome("a")); + + function crossover10 = fixedPointCrossover(1.0); + BOOST_CHECK(crossover10(Chromosome("fff"), Chromosome("a")) == Chromosome("f")); + BOOST_CHECK(crossover10(Chromosome("a"), Chromosome("fff")) == Chromosome("aff")); + + BOOST_CHECK(crossover10(Chromosome("f"), Chromosome("a")) == Chromosome("f")); +} + +BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_split_point_when_chromosome_empty) +{ + Chromosome empty(""); + Chromosome unsplittable("f"); + Chromosome splittable("aaaa"); + + function crossover00 = fixedPointCrossover(0.0); + BOOST_CHECK(crossover00(empty, empty) == empty); + BOOST_CHECK(crossover00(unsplittable, empty) == empty); + BOOST_CHECK(crossover00(empty, unsplittable) == unsplittable); + BOOST_CHECK(crossover00(splittable, empty) == empty); + BOOST_CHECK(crossover00(empty, splittable) == splittable); + + function crossover10 = fixedPointCrossover(1.0); + BOOST_CHECK(crossover10(empty, empty) == empty); + BOOST_CHECK(crossover10(unsplittable, empty) == empty); + BOOST_CHECK(crossover10(empty, unsplittable) == unsplittable); + BOOST_CHECK(crossover10(splittable, empty) == empty); + BOOST_CHECK(crossover10(empty, splittable) == splittable); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/PairSelections.cpp b/test/yulPhaser/PairSelections.cpp new file mode 100644 index 000000000..64109470f --- /dev/null +++ b/test/yulPhaser/PairSelections.cpp @@ -0,0 +1,185 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include + +#include + +#include +#include +#include + +using namespace std; + +namespace solidity::phaser::test +{ + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(PairSelectionsTest) +BOOST_AUTO_TEST_SUITE(RandomPairSelectionTest) + +BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabilities) +{ + constexpr int collectionSize = 10; + constexpr int selectionSize = 100; + constexpr double relativeTolerance = 0.1; + constexpr double expectedValue = (collectionSize - 1) / 2.0; + constexpr double variance = (collectionSize * collectionSize - 1) / 12.0; + + SimulationRNG::reset(1); + vector> pairs = RandomPairSelection(selectionSize).materialise(collectionSize); + vector samples; + for (auto& [first, second]: pairs) + { + samples.push_back(first); + samples.push_back(second); + } + + BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices) +{ + const size_t collectionSize = 200; + + vector> pairs = RandomPairSelection(0.5).materialise(collectionSize); + + BOOST_TEST(pairs.size() == 100); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<0>(pair) <= collectionSize; })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<1>(pair) <= collectionSize; })); +} + +BOOST_AUTO_TEST_CASE(materialise_should_never_return_a_pair_of_identical_indices) +{ + vector> pairs = RandomPairSelection(0.5).materialise(100); + + BOOST_TEST(pairs.size() == 50); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [](auto const& pair){ return get<0>(pair) != get<1>(pair); })); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_number_of_pairs_thats_a_fraction_of_collection_size) +{ + BOOST_TEST(RandomPairSelection(0.0).materialise(10).size() == 0); + BOOST_TEST(RandomPairSelection(0.3).materialise(10).size() == 3); + BOOST_TEST(RandomPairSelection(0.5).materialise(10).size() == 5); + BOOST_TEST(RandomPairSelection(0.7).materialise(10).size() == 7); + BOOST_TEST(RandomPairSelection(1.0).materialise(10).size() == 10); +} + +BOOST_AUTO_TEST_CASE(materialise_should_support_number_of_pairs_bigger_than_collection_size) +{ + BOOST_TEST(RandomPairSelection(2.0).materialise(5).size() == 10); + BOOST_TEST(RandomPairSelection(1.5).materialise(10).size() == 15); + BOOST_TEST(RandomPairSelection(10.0).materialise(10).size() == 100); +} + +BOOST_AUTO_TEST_CASE(materialise_should_round_the_number_of_pairs_to_the_nearest_integer) +{ + BOOST_TEST(RandomPairSelection(0.49).materialise(3).size() == 1); + BOOST_TEST(RandomPairSelection(0.50).materialise(3).size() == 2); + BOOST_TEST(RandomPairSelection(0.51).materialise(3).size() == 2); + + BOOST_TEST(RandomPairSelection(1.51).materialise(3).size() == 5); + + BOOST_TEST(RandomPairSelection(0.01).materialise(2).size() == 0); + BOOST_TEST(RandomPairSelection(0.01).materialise(3).size() == 0); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_is_empty) +{ + BOOST_TEST(RandomPairSelection(0).materialise(0).empty()); + BOOST_TEST(RandomPairSelection(0.5).materialise(0).empty()); + BOOST_TEST(RandomPairSelection(1.0).materialise(0).empty()); + BOOST_TEST(RandomPairSelection(2.0).materialise(0).empty()); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_has_one_element) +{ + BOOST_TEST(RandomPairSelection(0).materialise(1).empty()); + BOOST_TEST(RandomPairSelection(0.5).materialise(1).empty()); + BOOST_TEST(RandomPairSelection(1.0).materialise(1).empty()); + BOOST_TEST(RandomPairSelection(2.0).materialise(1).empty()); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(PairMosaicSelectionTest) + +using IndexPairs = vector>; + +BOOST_AUTO_TEST_CASE(materialise) +{ + BOOST_TEST(PairMosaicSelection({{1, 1}}, 0.5).materialise(4) == IndexPairs({{1, 1}, {1, 1}})); + BOOST_TEST(PairMosaicSelection({{1, 1}}, 1.0).materialise(4) == IndexPairs({{1, 1}, {1, 1}, {1, 1}, {1, 1}})); + BOOST_TEST(PairMosaicSelection({{1, 1}}, 2.0).materialise(4) == IndexPairs({{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}})); + BOOST_TEST(PairMosaicSelection({{1, 1}}, 1.0).materialise(2) == IndexPairs({{1, 1}, {1, 1}})); + + IndexPairs pairs1{{0, 1}, {1, 0}}; + BOOST_TEST(PairMosaicSelection(pairs1, 0.5).materialise(4) == IndexPairs({{0, 1}, {1, 0}})); + BOOST_TEST(PairMosaicSelection(pairs1, 1.0).materialise(4) == IndexPairs({{0, 1}, {1, 0}, {0, 1}, {1, 0}})); + BOOST_TEST(PairMosaicSelection(pairs1, 2.0).materialise(4) == IndexPairs({{0, 1}, {1, 0}, {0, 1}, {1, 0}, {0, 1}, {1, 0}, {0, 1}, {1, 0}})); + BOOST_TEST(PairMosaicSelection(pairs1, 1.0).materialise(2) == IndexPairs({{0, 1}, {1, 0}})); + + IndexPairs pairs2{{3, 2}, {2, 3}, {1, 0}, {1, 1}}; + BOOST_TEST(PairMosaicSelection(pairs2, 0.5).materialise(4) == IndexPairs({{3, 2}, {2, 3}})); + BOOST_TEST(PairMosaicSelection(pairs2, 1.0).materialise(4) == IndexPairs({{3, 2}, {2, 3}, {1, 0}, {1, 1}})); + BOOST_TEST(PairMosaicSelection(pairs2, 2.0).materialise(4) == IndexPairs({{3, 2}, {2, 3}, {1, 0}, {1, 1}, {3, 2}, {2, 3}, {1, 0}, {1, 1}})); + + IndexPairs pairs3{{1, 0}, {1, 1}, {1, 0}, {1, 1}}; + BOOST_TEST(PairMosaicSelection(pairs3, 1.0).materialise(2) == IndexPairs({{1, 0}, {1, 1}})); +} + +BOOST_AUTO_TEST_CASE(materialise_should_round_indices) +{ + IndexPairs pairs{{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + BOOST_TEST(PairMosaicSelection(pairs, 0.49).materialise(5) == IndexPairs({{4, 4}, {3, 3}})); + BOOST_TEST(PairMosaicSelection(pairs, 0.50).materialise(5) == IndexPairs({{4, 4}, {3, 3}, {2, 2}})); + BOOST_TEST(PairMosaicSelection(pairs, 0.51).materialise(5) == IndexPairs({{4, 4}, {3, 3}, {2, 2}})); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_is_empty) +{ + BOOST_TEST(PairMosaicSelection({{1, 1}}, 1.0).materialise(0).empty()); + BOOST_TEST(PairMosaicSelection({{1, 1}, {3, 3}}, 2.0).materialise(0).empty()); + BOOST_TEST(PairMosaicSelection({{5, 5}, {4, 4}, {3, 3}, {2, 2}}, 0.5).materialise(0).empty()); +} + +BOOST_AUTO_TEST_CASE(materialise_should_return_no_pairs_if_collection_has_one_element) +{ + IndexPairs pairs{{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + BOOST_TEST(PairMosaicSelection(pairs, 0.0).materialise(1).empty()); + BOOST_TEST(PairMosaicSelection(pairs, 0.5).materialise(1).empty()); + BOOST_TEST(PairMosaicSelection(pairs, 1.0).materialise(1).empty()); + BOOST_TEST(PairMosaicSelection(pairs, 7.0).materialise(1).empty()); +} + +BOOST_AUTO_TEST_CASE(materialise_should_clamp_indices_at_collection_size) +{ + IndexPairs pairs{{4, 4}, {3, 3}, {2, 2}, {1, 1}, {0, 0}}; + BOOST_TEST(PairMosaicSelection(pairs, 1.0).materialise(4) == IndexPairs({{3, 3}, {3, 3}, {2, 2}, {1, 1}})); + BOOST_TEST(PairMosaicSelection(pairs, 2.0).materialise(3) == IndexPairs({{2, 2}, {2, 2}, {2, 2}, {1, 1}, {0, 0}, {2, 2}})); + +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp new file mode 100644 index 000000000..633462865 --- /dev/null +++ b/test/yulPhaser/Phaser.cpp @@ -0,0 +1,444 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include + +#include + +#include + +#include +#include + +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::langutil; + +namespace fs = boost::filesystem; + +namespace solidity::phaser::test +{ + +class GeneticAlgorithmFactoryFixture +{ +protected: + GeneticAlgorithmFactory::Options m_options = { + /* algorithm = */ Algorithm::Random, + /* minChromosomeLength = */ 50, + /* maxChromosomeLength = */ 100, + /* randomElitePoolSize = */ 0.5, + /* gewepMutationPoolSize = */ 0.1, + /* gewepCrossoverPoolSize = */ 0.1, + /* gewepRandomisationChance = */ 0.6, + /* gewepDeletionVsAdditionChance = */ 0.3, + /* gewepGenesToRandomise = */ 0.4, + /* gewepGenesToAddOrDelete = */ 0.2, + }; +}; + +class FixtureWithPrograms +{ +protected: + vector m_sourceStreams = { + CharStream("{}", ""), + CharStream("{{}}", ""), + CharStream("{{{}}}", ""), + }; + vector m_programs = { + get(Program::load(m_sourceStreams[0])), + get(Program::load(m_sourceStreams[1])), + get(Program::load(m_sourceStreams[2])), + }; +}; + +class FitnessMetricFactoryFixture: public FixtureWithPrograms +{ +protected: + FitnessMetricFactory::Options m_options = { + /* metric = */ MetricChoice::CodeSize, + /* metricAggregator = */ MetricAggregatorChoice::Average, + /* relativeMetricScale = */ 5, + /* chromosomeRepetitions = */ 1, + }; +}; + +class PoulationFactoryFixture +{ +protected: + shared_ptr m_fitnessMetric = make_shared(); + PopulationFactory::Options m_options = { + /* minChromosomeLength = */ 0, + /* maxChromosomeLength = */ 0, + /* population = */ {}, + /* randomPopulation = */ {}, + /* populationFromFile = */ {}, + }; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(PhaserTest) +BOOST_AUTO_TEST_SUITE(GeneticAlgorithmFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_options_to_it, GeneticAlgorithmFactoryFixture) +{ + m_options.algorithm = Algorithm::Random; + unique_ptr algorithm1 = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm1 != nullptr); + + auto randomAlgorithm = dynamic_cast(algorithm1.get()); + BOOST_REQUIRE(randomAlgorithm != nullptr); + BOOST_TEST(randomAlgorithm->options().elitePoolSize == m_options.randomElitePoolSize.value()); + BOOST_TEST(randomAlgorithm->options().minChromosomeLength == m_options.minChromosomeLength); + BOOST_TEST(randomAlgorithm->options().maxChromosomeLength == m_options.maxChromosomeLength); + + m_options.algorithm = Algorithm::GEWEP; + unique_ptr algorithm2 = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm2 != nullptr); + + auto gewepAlgorithm = dynamic_cast(algorithm2.get()); + BOOST_REQUIRE(gewepAlgorithm != nullptr); + BOOST_TEST(gewepAlgorithm->options().mutationPoolSize == m_options.gewepMutationPoolSize); + BOOST_TEST(gewepAlgorithm->options().crossoverPoolSize == m_options.gewepCrossoverPoolSize); + BOOST_TEST(gewepAlgorithm->options().randomisationChance == m_options.gewepRandomisationChance); + BOOST_TEST(gewepAlgorithm->options().deletionVsAdditionChance == m_options.gewepDeletionVsAdditionChance); + BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == m_options.gewepGenesToRandomise.value()); + BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == m_options.gewepGenesToAddOrDelete.value()); +} + +BOOST_FIXTURE_TEST_CASE(build_should_set_random_algorithm_elite_pool_size_based_on_population_size_if_not_specified, GeneticAlgorithmFactoryFixture) +{ + m_options.algorithm = Algorithm::Random; + m_options.randomElitePoolSize = nullopt; + unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm != nullptr); + + auto randomAlgorithm = dynamic_cast(algorithm.get()); + BOOST_REQUIRE(randomAlgorithm != nullptr); + BOOST_TEST(randomAlgorithm->options().elitePoolSize == 1.0 / 100.0); +} + +BOOST_FIXTURE_TEST_CASE(build_should_set_gewep_mutation_percentages_based_on_maximum_chromosome_length_if_not_specified, GeneticAlgorithmFactoryFixture) +{ + m_options.algorithm = Algorithm::GEWEP; + m_options.gewepGenesToRandomise = nullopt; + m_options.gewepGenesToAddOrDelete = nullopt; + m_options.maxChromosomeLength = 125; + + unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); + BOOST_REQUIRE(algorithm != nullptr); + + auto gewepAlgorithm = dynamic_cast(algorithm.get()); + BOOST_REQUIRE(gewepAlgorithm != nullptr); + BOOST_TEST(gewepAlgorithm->options().percentGenesToRandomise == 1.0 / 125.0); + BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == 1.0 / 125.0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(FitnessMetricFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::RelativeCodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Sum; + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); + BOOST_REQUIRE(metric != nullptr); + + auto sumMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(sumMetric != nullptr); + BOOST_REQUIRE(sumMetric->metrics().size() == 1); + BOOST_REQUIRE(sumMetric->metrics()[0] != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(sumMetric->metrics()[0].get()); + BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); + BOOST_TEST(toString(relativeProgramSizeMetric->program()) == toString(m_programs[0])); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::CodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Average; + m_options.chromosomeRepetitions = 5; + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); + BOOST_REQUIRE(metric != nullptr); + + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto programSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); + BOOST_REQUIRE(programSizeMetric != nullptr); + BOOST_TEST(programSizeMetric->repetitionCount() == m_options.chromosomeRepetitions); +} + +BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFactoryFixture) +{ + m_options.metric = MetricChoice::RelativeCodeSize; + m_options.metricAggregator = MetricAggregatorChoice::Average; + m_options.relativeMetricScale = 10; + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); + BOOST_REQUIRE(metric != nullptr); + + auto averageMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(averageMetric != nullptr); + BOOST_REQUIRE(averageMetric->metrics().size() == 1); + BOOST_REQUIRE(averageMetric->metrics()[0] != nullptr); + + auto relativeProgramSizeMetric = dynamic_cast(averageMetric->metrics()[0].get()); + BOOST_REQUIRE(relativeProgramSizeMetric != nullptr); + BOOST_TEST(relativeProgramSizeMetric->fixedPointPrecision() == m_options.relativeMetricScale); +} + +BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture) +{ + unique_ptr metric = FitnessMetricFactory::build( + m_options, + m_programs, + vector>(m_programs.size(), nullptr) + ); + BOOST_REQUIRE(metric != nullptr); + + auto combinedMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(combinedMetric != nullptr); + BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size()); +} + +BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetricFactoryFixture) +{ + assert(m_programs.size() == 3); + vector> caches = { + make_shared(m_programs[0]), + make_shared(m_programs[1]), + make_shared(m_programs[2]), + }; + + m_options.metric = MetricChoice::RelativeCodeSize; + unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs, caches); + BOOST_REQUIRE(metric != nullptr); + + auto combinedMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(combinedMetric != nullptr); + BOOST_REQUIRE(combinedMetric->metrics().size() == caches.size()); + + for (size_t i = 0; i < caches.size(); ++i) + { + auto programBasedMetric = dynamic_cast(combinedMetric->metrics()[i].get()); + BOOST_REQUIRE(programBasedMetric != nullptr); + BOOST_TEST(programBasedMetric->programCache() == caches[i].get()); + } +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(PopulationFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_an_empty_population_if_no_specific_options_given, PoulationFactoryFixture) +{ + m_options.population = {}; + m_options.randomPopulation = {}; + m_options.populationFromFile = {}; + BOOST_TEST( + PopulationFactory::build(m_options, m_fitnessMetric) == + Population(m_fitnessMetric, vector{}) + ); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_population_option, PoulationFactoryFixture) +{ + m_options.population = {"a", "afc", "xadd"}; + BOOST_TEST( + PopulationFactory::build(m_options, m_fitnessMetric) == + Population(m_fitnessMetric, {Chromosome("a"), Chromosome("afc"), Chromosome("xadd")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_random_population_option, PoulationFactoryFixture) +{ + m_options.randomPopulation = {5, 3, 2}; + m_options.minChromosomeLength = 5; + m_options.maxChromosomeLength = 10; + + auto population = PopulationFactory::build(m_options, m_fitnessMetric); + + BOOST_TEST(population.individuals().size() == 10); + BOOST_TEST(all_of( + population.individuals().begin(), + population.individuals().end(), + [](auto const& individual){ return 5 <= individual.chromosome.length() && individual.chromosome.length() <= 10; } + )); +} + +BOOST_FIXTURE_TEST_CASE(build_should_respect_population_from_file_option, PoulationFactoryFixture) +{ + map> fileContent = { + {"a.txt", {"a", "fff", "", "jxccLTa"}}, + {"b.txt", {}}, + {"c.txt", {""}}, + {"d.txt", {"c", "T"}}, + }; + + TemporaryDirectory tempDir; + for (auto const& [fileName, chromosomes]: fileContent) + { + ofstream tmpFile(tempDir.memberPath(fileName)); + for (auto const& chromosome: chromosomes) + tmpFile << chromosome << endl; + + m_options.populationFromFile.push_back(tempDir.memberPath(fileName)); + } + + BOOST_TEST( + PopulationFactory::build(m_options, m_fitnessMetric) == + Population(m_fitnessMetric, { + Chromosome("a"), + Chromosome("fff"), + Chromosome(""), + Chromosome("jxccLTa"), + Chromosome(""), + Chromosome("c"), + Chromosome("T"), + }) + ); +} + +BOOST_FIXTURE_TEST_CASE(build_should_throw_FileOpenError_if_population_file_does_not_exist, PoulationFactoryFixture) +{ + m_options.populationFromFile = {"a-file-that-does-not-exist.abcdefgh"}; + assert(!fs::exists(m_options.populationFromFile[0])); + + BOOST_CHECK_THROW(PopulationFactory::build(m_options, m_fitnessMetric), FileOpenError); +} + +BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, PoulationFactoryFixture) +{ + TemporaryDirectory tempDir; + { + ofstream tmpFile(tempDir.memberPath("population.txt")); + tmpFile << "axc" << endl << "fcL" << endl; + } + + m_options.population = {"axc", "fcL"}; + m_options.randomPopulation = {2}; + m_options.populationFromFile = {tempDir.memberPath("population.txt")}; + m_options.minChromosomeLength = 3; + m_options.maxChromosomeLength = 3; + + auto population = PopulationFactory::build(m_options, m_fitnessMetric); + + auto begin = population.individuals().begin(); + auto end = population.individuals().end(); + BOOST_TEST(population.individuals().size() == 6); + BOOST_TEST(all_of(begin, end, [](auto const& individual){ return individual.chromosome.length() == 3; })); + BOOST_TEST(count(begin, end, Individual(Chromosome("axc"), *m_fitnessMetric)) >= 2); + BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2); +} + + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(ProgramCacheFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cache_enabled, FixtureWithPrograms) +{ + ProgramCacheFactory::Options options{/* programCacheEnabled = */ true}; + vector> caches = ProgramCacheFactory::build(options, m_programs); + assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); + + BOOST_TEST(caches.size() == m_programs.size()); + for (size_t i = 0; i < m_programs.size(); ++i) + { + BOOST_REQUIRE(caches[i] != nullptr); + BOOST_TEST(toString(caches[i]->program()) == toString(m_programs[i])); + } +} + +BOOST_FIXTURE_TEST_CASE(build_should_return_nullptr_for_each_input_program_if_cache_disabled, FixtureWithPrograms) +{ + ProgramCacheFactory::Options options{/* programCacheEnabled = */ false}; + vector> caches = ProgramCacheFactory::build(options, m_programs); + assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); + + BOOST_TEST(caches.size() == m_programs.size()); + for (size_t i = 0; i < m_programs.size(); ++i) + BOOST_TEST(caches[i] == nullptr); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(ProgramFactoryTest) + +BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) +{ + TemporaryDirectory tempDir; + vector sources{"{}", "{{}}", "{{{}}}"}; + ProgramFactory::Options options{ + /* inputFiles = */ { + tempDir.memberPath("program1.yul"), + tempDir.memberPath("program2.yul"), + tempDir.memberPath("program3.yul"), + }, + /* prefix = */ "", + }; + + for (size_t i = 0; i < sources.size(); ++i) + { + ofstream tmpFile(options.inputFiles[i]); + tmpFile << sources[i] << endl; + } + + vector programs = ProgramFactory::build(options); + + BOOST_TEST(programs.size() == sources.size()); + for (size_t i = 0; i < sources.size(); ++i) + { + CharStream sourceStream(sources[i], options.inputFiles[i]); + BOOST_TEST(toString(programs[i]) == toString(get(Program::load(sourceStream)))); + } +} + +BOOST_AUTO_TEST_CASE(build_should_apply_prefix) +{ + TemporaryDirectory tempDir; + ProgramFactory::Options options{ + /* inputFiles = */ {tempDir.memberPath("program1.yul")}, + /* prefix = */ "f", + }; + + CharStream nestedSource("{{{let x:= 1}}}", ""); + Program nestedProgram = get(Program::load(nestedSource)); + Program flatProgram = get(Program::load(nestedSource)); + flatProgram.optimise(Chromosome("f").optimisationSteps()); + assert(toString(nestedProgram) != toString(flatProgram)); + + { + ofstream tmpFile(options.inputFiles[0]); + tmpFile << nestedSource.source() << endl; + } + + vector programs = ProgramFactory::build(options); + + BOOST_TEST(programs.size() == 1); + BOOST_TEST(toString(programs[0]) == toString(flatProgram)); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 98532c9dc..7a9172749 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -15,9 +15,10 @@ along with solidity. If not, see . */ -#include +#include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include +#include #include #include #include @@ -226,6 +228,70 @@ BOOST_FIXTURE_TEST_CASE(select_should_return_empty_population_if_selection_is_em BOOST_TEST(population.select(selection).individuals().empty()); } +BOOST_FIXTURE_TEST_CASE(mutate_should_return_population_containing_individuals_indicated_by_selection_with_mutation_applied, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); + RangeSelection selection(0.25, 0.75); + assert(selection.materialise(population.individuals().size()) == (vector{1, 2})); + + Population expectedPopulation(m_fitnessMetric, {Chromosome("fc"), Chromosome("fg")}); + + BOOST_TEST(population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == expectedPopulation); +} + +BOOST_FIXTURE_TEST_CASE(mutate_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); + RangeSelection selection(0.0, 1.0); + assert(selection.materialise(population.individuals().size()) == (vector{0, 1})); + + BOOST_TEST( + population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == + Population(m_fitnessMetric, {Chromosome("fa"), Chromosome("fa")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(mutate_should_return_empty_population_if_selection_is_empty, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc")}); + RangeSelection selection(0.0, 0.0); + assert(selection.materialise(population.individuals().size()).empty()); + + BOOST_TEST(population.mutate(selection, geneSubstitution(0, BlockFlattener::name)).individuals().empty()); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individuals_indicated_by_selection_with_crossover_applied, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); + PairMosaicSelection selection({{0, 1}, {2, 1}}, 1.0); + assert(selection.materialise(population.individuals().size()) == (vector>{{0, 1}, {2, 1}, {0, 1}, {2, 1}})); + + Population expectedPopulation(m_fitnessMetric, {Chromosome("ac"), Chromosome("ac"), Chromosome("gc"), Chromosome("gc")}); + + BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)) == expectedPopulation); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_include_duplicates_if_selection_contains_duplicates, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); + PairMosaicSelection selection({{0, 0}, {1, 1}}, 2.0); + assert(selection.materialise(population.individuals().size()) == (vector>{{0, 0}, {1, 1}, {0, 0}, {1, 1}})); + + BOOST_TEST( + population.crossover(selection, fixedPointCrossover(0.5)) == + Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa"), Chromosome("aa"), Chromosome("aa")}) + ); +} + +BOOST_FIXTURE_TEST_CASE(crossover_should_return_empty_population_if_selection_is_empty, PopulationFixture) +{ + Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc")}); + PairMosaicSelection selection({}, 0.0); + assert(selection.materialise(population.individuals().size()).empty()); + + BOOST_TEST(population.crossover(selection, fixedPointCrossover(0.5)).individuals().empty()); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp index 7d9c81cd3..e69f0fc5c 100644 --- a/test/yulPhaser/Program.cpp +++ b/test/yulPhaser/Program.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); Program programCopy(program); @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream) CharStream sourceStream(sourceCode, current_test_case().p_name); sourceStream.setPosition(5); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); BOOST_TEST(CodeSize::codeSize(program.ast()) == 2); } @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(load_should_disambiguate) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. Block const& parentBlock = skipRedundantBlocks(program.ast()); @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); BOOST_TEST(program.ast().statements.size() == 3); BOOST_TEST(holds_alternative(program.ast().statements[0])); @@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. Block const& parentBlock = skipRedundantBlocks(program.ast()); @@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_parsed) string sourceCode("invalid program\n"); CharStream sourceStream(sourceCode, current_test_case().p_name); - BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram); + BOOST_TEST(holds_alternative(Program::load(sourceStream))); } BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyzed) @@ -186,7 +186,133 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyze ); CharStream sourceStream(sourceCode, current_test_case().p_name); - BOOST_CHECK_THROW(Program::load(sourceStream), InvalidProgram); + BOOST_TEST(holds_alternative(Program::load(sourceStream))); +} + +BOOST_AUTO_TEST_CASE(load_should_accept_yul_objects_as_input) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_return_errors_if_analysis_of_object_code_fails) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_return_errors_if_parsing_of_nested_object_fails) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + " object \"duplicate_name\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " }\n" + " object \"duplicate_name\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_extract_nested_object_with_deployed_suffix_if_present) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + " object \"C_178_deployed\" {\n" + " code {\n" + " mstore(64, 128)\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_fall_back_to_parsing_the_whole_object_if_there_is_no_subobject_with_the_right_name) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " object \"subobject\" {\n" + " code {\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + " object \"C_177_deployed\" {\n" + " code {\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); + + Block const& parentBlock = skipRedundantBlocks(get(programOrErrors).ast()); + BOOST_TEST(parentBlock.statements.size() == 1); + BOOST_TEST(holds_alternative(parentBlock.statements[0])); +} + +BOOST_AUTO_TEST_CASE(load_should_ignore_data_in_objects) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " data \"C_178_deployed\" hex\"4123\"\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(optimise) @@ -200,7 +326,7 @@ BOOST_AUTO_TEST_CASE(optimise) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); [[maybe_unused]] Block const& parentBlockBefore = skipRedundantBlocks(program.ast()); assert(parentBlockBefore.statements.size() == 2); @@ -231,7 +357,7 @@ BOOST_AUTO_TEST_CASE(output_operator) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); // NOTE: The snippet above was chosen so that the few optimisations applied automatically by load() // as of now do not change the code significantly. If that changes, you may have to update it. @@ -250,7 +376,7 @@ BOOST_AUTO_TEST_CASE(toJson) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); Json::Value parsingResult; string errors; @@ -270,7 +396,7 @@ BOOST_AUTO_TEST_CASE(codeSize) "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - auto program = Program::load(sourceStream); + Program program = get(Program::load(sourceStream)); BOOST_TEST(program.codeSize() == CodeSize::codeSizeIncludingFunctions(program.ast())); } diff --git a/test/yulPhaser/ProgramCache.cpp b/test/yulPhaser/ProgramCache.cpp new file mode 100644 index 000000000..1ed6024bc --- /dev/null +++ b/test/yulPhaser/ProgramCache.cpp @@ -0,0 +1,255 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; + +namespace solidity::phaser::test +{ + +class ProgramCacheFixture +{ +protected: + static constexpr char SampleSourceCode[] = + "{\n" + " for { let i := 0 } not(eq(i, 15)) { i := add(i, 1) }\n" + " {\n" + " let x := 1\n" + " mstore(i, 2)\n" + " }\n" + "}\n"; + + Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const + { + Program result = move(_program); + result.optimise(Chromosome(_abbreviatedOptimisationSteps).optimisationSteps()); + return result; + } + + static set cachedKeys(ProgramCache const& _programCache) + { + set keys; + for (auto pair = _programCache.entries().begin(); pair != _programCache.entries().end(); ++pair) + keys.insert(pair->first); + + return keys; + } + + CharStream m_sourceStream = CharStream(SampleSourceCode, "program-cache-test"); + Program m_program = get(Program::load(m_sourceStream)); + ProgramCache m_programCache{m_program}; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(ProgramCacheTest) + +BOOST_AUTO_TEST_CASE(CacheStats_operator_plus_should_add_stats_together) +{ + CacheStats statsA{11, 12, 13, {{1, 14}, {2, 15}}}; + CacheStats statsB{21, 22, 23, {{2, 24}, {3, 25}}}; + CacheStats statsC{32, 34, 36, {{1, 14}, {2, 39}, {3, 25}}}; + + BOOST_CHECK(statsA + statsB == statsC); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture) +{ + Program expectedProgram = optimisedProgram(m_program, "IuO"); + assert(toString(expectedProgram) != toString(m_program)); + + Program cachedProgram = m_programCache.optimiseProgram("IuO"); + + BOOST_TEST(toString(cachedProgram) == toString(expectedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, ProgramCacheFixture) +{ + Program programI = optimisedProgram(m_program, "I"); + Program programIu = optimisedProgram(programI, "u"); + Program programIuO = optimisedProgram(programIu, "O"); + assert(toString(m_program) != toString(programI)); + assert(toString(m_program) != toString(programIu)); + assert(toString(m_program) != toString(programIuO)); + assert(toString(programI) != toString(programIu)); + assert(toString(programI) != toString(programIuO)); + assert(toString(programIu) != toString(programIuO)); + + BOOST_REQUIRE(m_programCache.size() == 0); + + Program cachedProgram = m_programCache.optimiseProgram("IuO"); + + BOOST_TEST(toString(cachedProgram) == toString(programIuO)); + + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + BOOST_TEST(toString(*m_programCache.find("I")) == toString(programI)); + BOOST_TEST(toString(*m_programCache.find("Iu")) == toString(programIu)); + BOOST_TEST(toString(*m_programCache.find("IuO")) == toString(programIuO)); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_repeat_the_chromosome_requested_number_of_times, ProgramCacheFixture) +{ + string steps = "IuOIuO"; + + Program cachedProgram = m_programCache.optimiseProgram("IuO", 2); + + ProgramCache cacheNoRepetitions(m_program); + Program cachedProgramNoRepetitions = cacheNoRepetitions.optimiseProgram("IuOIuO"); + + BOOST_TEST(toString(cachedProgram) == toString(cachedProgramNoRepetitions)); + + for (size_t size = 1; size <= 6; ++size) + { + BOOST_REQUIRE(m_programCache.contains(steps.substr(0, size))); + BOOST_REQUIRE(cacheNoRepetitions.contains(steps.substr(0, size))); + BOOST_TEST( + toString(*cacheNoRepetitions.find(steps.substr(0, size))) == + toString(*m_programCache.find(steps.substr(0, size))) + ); + } +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_reuse_the_longest_prefix_and_move_it_to_the_next_round, ProgramCacheFixture) +{ + BOOST_TEST(m_programCache.currentRound() == 0); + + m_programCache.optimiseProgram("Iu"); + m_programCache.optimiseProgram("Ia"); + m_programCache.startRound(1); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); + + m_programCache.optimiseProgram("IuOI"); + + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia", "IuO", "IuOI"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("IuO")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("IuOI")->second.roundNumber == 1); +} + +BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, ProgramCacheFixture) +{ + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_TEST(m_programCache.size() == 0); + + m_programCache.optimiseProgram("Iu"); + + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + + m_programCache.optimiseProgram("a"); + + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); + + m_programCache.startRound(1); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); + + m_programCache.optimiseProgram("af"); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a", "af"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); + + m_programCache.startRound(2); + + BOOST_TEST(m_programCache.currentRound() == 2); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"a", "af"})); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); + + m_programCache.startRound(3); + + BOOST_TEST(m_programCache.currentRound() == 3); + BOOST_TEST(m_programCache.size() == 0); +} + +BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCacheFixture) +{ + size_t sizeI = optimisedProgram(m_program, "I").codeSize(); + size_t sizeIu = optimisedProgram(m_program, "Iu").codeSize(); + size_t sizeIuO = optimisedProgram(m_program, "IuO").codeSize(); + size_t sizeL = optimisedProgram(m_program, "L").codeSize(); + size_t sizeLT = optimisedProgram(m_program, "LT").codeSize(); + + m_programCache.optimiseProgram("L"); + m_programCache.optimiseProgram("Iu"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu"})); + CacheStats expectedStats1{0, 3, sizeL + sizeI + sizeIu, {{0, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats1); + + m_programCache.optimiseProgram("IuO"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + CacheStats expectedStats2{2, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 4}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); + + m_programCache.startRound(1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); + + m_programCache.optimiseProgram("IuO"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + CacheStats expectedStats3{5, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 1}, {1, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats3); + + m_programCache.startRound(2); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + CacheStats expectedStats4{5, 4, sizeI + sizeIu + sizeIuO, {{1, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats4); + + m_programCache.optimiseProgram("LT"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "LT", "I", "Iu", "IuO"})); + CacheStats expectedStats5{5, 6, sizeL + sizeLT + sizeI + sizeIu + sizeIuO, {{1, 3}, {2, 2}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats5); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/yulPhaser/Selections.cpp b/test/yulPhaser/Selections.cpp index ce870ce80..02a85f4f3 100644 --- a/test/yulPhaser/Selections.cpp +++ b/test/yulPhaser/Selections.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include #include diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index f158fcbf6..0cdacc428 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see . */ -#include +#include #include diff --git a/test/yulPhaser/TestHelpers.cpp b/test/yulPhaser/TestHelpers.cpp new file mode 100644 index 000000000..2379016fe --- /dev/null +++ b/test/yulPhaser/TestHelpers.cpp @@ -0,0 +1,133 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; +using namespace solidity::phaser; +using namespace solidity::phaser::test; + +namespace fs = boost::filesystem; + +function phaser::test::wholeChromosomeReplacement(Chromosome _newChromosome) +{ + return [_newChromosome = move(_newChromosome)](Chromosome const&) { return _newChromosome; }; +} + +function phaser::test::geneSubstitution(size_t _geneIndex, string _geneValue) +{ + return [=](Chromosome const& _chromosome) + { + vector newGenes = _chromosome.optimisationSteps(); + assert(_geneIndex < newGenes.size()); + newGenes[_geneIndex] = _geneValue; + + return Chromosome(newGenes); + }; +} + +vector phaser::test::chromosomeLengths(Population const& _population) +{ + vector lengths; + for (auto const& individual: _population.individuals()) + lengths.push_back(individual.chromosome.length()); + + return lengths; +} + +map phaser::test::enumerateOptmisationSteps() +{ + map stepIndices; + size_t i = 0; + for (auto const& nameAndAbbreviation: OptimiserSuite::stepNameToAbbreviationMap()) + stepIndices.insert({nameAndAbbreviation.first, i++}); + + return stepIndices; +} + +size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome const& _chromosome2) +{ + size_t count = 0; + for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i) + count += static_cast(_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]); + + return count + abs(static_cast(_chromosome1.length() - _chromosome2.length())); +} + +TemporaryDirectory::TemporaryDirectory(std::string const& _prefix): + m_path((fs::temp_directory_path() / fs::unique_path(_prefix + "%%%%-%%%%-%%%%-%%%%")).string()) +{ + // Prefix should just be a file name and not contain anything that would make us step out of /tmp. + assert(fs::path(_prefix) == fs::path(_prefix).stem()); + + fs::create_directory(m_path); +} + +TemporaryDirectory::~TemporaryDirectory() +{ + // A few paranoid sanity checks just to be extra sure we're not deleting someone's homework. + assert(m_path.find(fs::temp_directory_path().string()) == 0); + assert(fs::path(m_path) != fs::temp_directory_path()); + assert(fs::path(m_path) != fs::path(m_path).root_path()); + assert(!fs::path(m_path).empty()); + + boost::system::error_code errorCode; + uintmax_t numRemoved = fs::remove_all(m_path, errorCode); + if (errorCode.value() != boost::system::errc::success) + { + cerr << "Failed to completely remove temporary directory '" << m_path << "'. "; + cerr << "Only " << numRemoved << " files were actually removed." << endl; + cerr << "Reason: " << errorCode.message() << endl; + } +} + +string TemporaryDirectory::memberPath(string const& _relativePath) const +{ + assert(fs::path(_relativePath).is_relative()); + + return (fs::path(m_path) / _relativePath).string(); +} + +string phaser::test::stripWhitespace(string const& input) +{ + regex whitespaceRegex("\\s+"); + return regex_replace(input, whitespaceRegex, ""); +} + +size_t phaser::test::countSubstringOccurrences(string const& _inputString, string const& _substring) +{ + assert(_substring.size() > 0); + + size_t count = 0; + size_t lastOccurrence = 0; + while ((lastOccurrence = _inputString.find(_substring, lastOccurrence)) != string::npos) + { + ++count; + lastOccurrence += _substring.size(); + } + + return count; +} diff --git a/test/yulPhaser/Common.h b/test/yulPhaser/TestHelpers.h similarity index 72% rename from test/yulPhaser/Common.h rename to test/yulPhaser/TestHelpers.h index e73260d7a..2bf755b18 100644 --- a/test/yulPhaser/Common.h +++ b/test/yulPhaser/TestHelpers.h @@ -30,9 +30,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -49,19 +51,59 @@ class ChromosomeLengthMetric: public FitnessMetric { public: using FitnessMetric::FitnessMetric; - size_t evaluate(Chromosome const& _chromosome) const override { return _chromosome.length(); } + size_t evaluate(Chromosome const& _chromosome) override { return _chromosome.length(); } }; +// MUTATIONS + +/// Mutation that always replaces the whole chromosome with the one specified in the parameter. +std::function wholeChromosomeReplacement(Chromosome _newChromosome); + +/// Mutation that always replaces the optimisation step at position @a _geneIndex with @a _geneValue. +/// +/// The chromosome must be long enough for this position to exist. +std::function geneSubstitution(size_t _geneIndex, std::string _geneValue); + // CHROMOSOME AND POPULATION HELPERS /// Returns a vector containing lengths of all chromosomes in the population (in the same order). std::vector chromosomeLengths(Population const& _population); +/// Returns the number of genes that differ between two chromosomes. +/// If the chromnosomes have different lengths, the positions that are present in only one of them +/// are counted as mismatches. +size_t countDifferences(Chromosome const& _chromosome1, Chromosome const& _chromosome2); + /// Assigns indices from 0 to N to all optimisation steps available in the OptimiserSuite. /// This is a convenience helper to make it easier to test their distribution with tools made for /// integers. std::map enumerateOptmisationSteps(); +// FILESYSTEM UTILITIES + +/** + * An object that creates a unique temporary directory and automatically deletes it and its + * content upon being destroyed. + * + * The directory is guaranteed to be newly created and empty. Directory names are generated + * randomly. If a directory with the same name already exists (very unlikely but possible) the + * object won't reuse it and will fail with an exception instead. + */ +class TemporaryDirectory +{ +public: + TemporaryDirectory(std::string const& _prefix = "yul-phaser-test-"); + ~TemporaryDirectory(); + + std::string const& path() const { return m_path; } + + /// Converts a path relative to the directory held by the object into an absolute one. + std::string memberPath(std::string const& _relativePath) const; + +private: + std::string m_path; +}; + // STRING UTILITIES /// Returns the input string with all the whitespace characters (spaces, line endings, etc.) removed. diff --git a/test/yulPhaser/CommonTest.cpp b/test/yulPhaser/TestHelpersTest.cpp similarity index 54% rename from test/yulPhaser/CommonTest.cpp rename to test/yulPhaser/TestHelpersTest.cpp index 9bcab9923..7fc4ef0af 100644 --- a/test/yulPhaser/CommonTest.cpp +++ b/test/yulPhaser/TestHelpersTest.cpp @@ -15,23 +15,27 @@ along with solidity. If not, see . */ -#include +#include #include +#include #include +#include #include using namespace std; using namespace solidity::yul; using namespace boost::test_tools; +namespace fs = boost::filesystem; + namespace solidity::phaser::test { BOOST_AUTO_TEST_SUITE(Phaser) -BOOST_AUTO_TEST_SUITE(CommonTest) +BOOST_AUTO_TEST_SUITE(TestHelpersTest) BOOST_AUTO_TEST_CASE(ChromosomeLengthMetric_evaluate_should_return_chromosome_length) { @@ -40,6 +44,23 @@ BOOST_AUTO_TEST_CASE(ChromosomeLengthMetric_evaluate_should_return_chromosome_le BOOST_TEST(ChromosomeLengthMetric{}.evaluate(Chromosome("aaaaa")) == 5); } +BOOST_AUTO_TEST_CASE(wholeChromosomeReplacement_should_replace_whole_chromosome_with_another) +{ + function mutation = wholeChromosomeReplacement(Chromosome("aaa")); + BOOST_TEST(mutation(Chromosome("ccc")) == Chromosome("aaa")); +} + +BOOST_AUTO_TEST_CASE(geneSubstitution_should_change_a_single_gene_at_a_given_index) +{ + Chromosome chromosome("aaccff"); + + function mutation1 = geneSubstitution(0, chromosome.optimisationSteps()[5]); + BOOST_TEST(mutation1(chromosome) == Chromosome("faccff")); + + function mutation2 = geneSubstitution(5, chromosome.optimisationSteps()[0]); + BOOST_TEST(mutation2(chromosome) == Chromosome("aaccfa")); +} + BOOST_AUTO_TEST_CASE(chromosomeLengths_should_return_lengths_of_all_chromosomes_in_a_population) { shared_ptr fitnessMetric = make_shared(); @@ -51,6 +72,34 @@ BOOST_AUTO_TEST_CASE(chromosomeLengths_should_return_lengths_of_all_chromosomes_ BOOST_TEST((chromosomeLengths(population2) == vector{})); } +BOOST_AUTO_TEST_CASE(countDifferences_should_return_zero_for_identical_chromosomes) +{ + BOOST_TEST(countDifferences(Chromosome(), Chromosome()) == 0); + BOOST_TEST(countDifferences(Chromosome("a"), Chromosome("a")) == 0); + BOOST_TEST(countDifferences(Chromosome("afxT"), Chromosome("afxT")) == 0); +} + +BOOST_AUTO_TEST_CASE(countDifferences_should_count_mismatched_positions_in_chromosomes_of_the_same_length) +{ + BOOST_TEST(countDifferences(Chromosome("a"), Chromosome("f")) == 1); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("ac")) == 1); + BOOST_TEST(countDifferences(Chromosome("ac"), Chromosome("cc")) == 1); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("cc")) == 2); + BOOST_TEST(countDifferences(Chromosome("afxT"), Chromosome("Txfa")) == 4); +} + +BOOST_AUTO_TEST_CASE(countDifferences_should_count_missing_characters_as_differences) +{ + BOOST_TEST(countDifferences(Chromosome(""), Chromosome("a")) == 1); + BOOST_TEST(countDifferences(Chromosome("a"), Chromosome("")) == 1); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("")) == 2); + BOOST_TEST(countDifferences(Chromosome("aaa"), Chromosome("")) == 3); + + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("aaaa")) == 2); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("aacc")) == 2); + BOOST_TEST(countDifferences(Chromosome("aa"), Chromosome("cccc")) == 4); +} + BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_available_optimisation_steps) { map stepsAndAbbreviations = OptimiserSuite::stepNameToAbbreviationMap(); @@ -69,6 +118,63 @@ BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_ava } } +BOOST_AUTO_TEST_CASE(TemporaryDirectory_should_create_and_delete_a_unique_and_empty_directory) +{ + fs::path dirPath; + { + TemporaryDirectory tempDir("temporary-directory-test-"); + dirPath = tempDir.path(); + + BOOST_TEST(dirPath.stem().string().find("temporary-directory-test-") == 0); + BOOST_TEST(fs::equivalent(dirPath.parent_path(), fs::temp_directory_path())); + BOOST_TEST(fs::is_directory(dirPath)); + BOOST_TEST(fs::is_empty(dirPath)); + } + BOOST_TEST(!fs::exists(dirPath)); +} + +BOOST_AUTO_TEST_CASE(TemporaryDirectory_should_delete_its_directory_even_if_not_empty) +{ + fs::path dirPath; + { + TemporaryDirectory tempDir("temporary-directory-test-"); + dirPath = tempDir.path(); + + BOOST_TEST(fs::is_directory(dirPath)); + + { + ofstream tmpFile((dirPath / "test-file.txt").string()); + tmpFile << "Delete me!" << endl; + } + assert(fs::is_regular_file(dirPath / "test-file.txt")); + } + BOOST_TEST(!fs::exists(dirPath / "test-file.txt")); +} + +BOOST_AUTO_TEST_CASE(TemporaryDirectory_memberPath_should_construct_paths_relative_to_the_temporary_directory) +{ + TemporaryDirectory tempDir("temporary-directory-test-"); + + BOOST_TEST(fs::equivalent(tempDir.memberPath(""), tempDir.path())); + BOOST_TEST(fs::equivalent(tempDir.memberPath("."), tempDir.path() / fs::path("."))); + BOOST_TEST(fs::equivalent(tempDir.memberPath(".."), tempDir.path() / fs::path(".."))); + + // NOTE: fs::equivalent() only works with paths that actually exist + { + ofstream file; + file.open(tempDir.memberPath("file.txt"), ios::out); + } + BOOST_TEST(fs::equivalent(tempDir.memberPath("file.txt"), tempDir.path() / fs::path("file.txt"))); + + { + fs::create_directories(tempDir.memberPath("a/b/")); + + ofstream file; + file.open(tempDir.memberPath("a/b/file.txt"), ios::out); + } + BOOST_TEST(fs::equivalent(tempDir.memberPath("a/b/file.txt"), tempDir.path() / fs::path("a") / fs::path("b") / fs::path("file.txt"))); +} + BOOST_AUTO_TEST_CASE(stripWhitespace_should_remove_all_whitespace_characters_from_a_string) { BOOST_TEST(stripWhitespace("") == ""); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 21c0b8c91..241a80f46 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -15,6 +15,12 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}") add_executable(yul-phaser yulPhaser/main.cpp + yulPhaser/Common.h + yulPhaser/Common.cpp + yulPhaser/AlgorithmRunner.h + yulPhaser/AlgorithmRunner.cpp + yulPhaser/Phaser.h + yulPhaser/Phaser.cpp yulPhaser/GeneticAlgorithms.h yulPhaser/GeneticAlgorithms.cpp yulPhaser/Population.h @@ -23,8 +29,14 @@ add_executable(yul-phaser yulPhaser/FitnessMetrics.cpp yulPhaser/Chromosome.h yulPhaser/Chromosome.cpp + yulPhaser/Mutations.h + yulPhaser/Mutations.cpp + yulPhaser/PairSelections.h + yulPhaser/PairSelections.cpp yulPhaser/Selections.h yulPhaser/Selections.cpp + yulPhaser/ProgramCache.h + yulPhaser/ProgramCache.cpp yulPhaser/Program.h yulPhaser/Program.cpp yulPhaser/SimulationRNG.h diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp new file mode 100644 index 000000000..c402e5d84 --- /dev/null +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -0,0 +1,202 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity::phaser; + +void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) +{ + populationAutosave(); + printInitialPopulation(); + cacheClear(); + + clock_t totalTimeStart = clock(); + for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round) + { + clock_t roundTimeStart = clock(); + cacheStartRound(round + 1); + + m_population = _algorithm.runNextRound(m_population); + randomiseDuplicates(); + + printRoundSummary(round, roundTimeStart, totalTimeStart); + printCacheStats(); + populationAutosave(); + } +} + +void AlgorithmRunner::printRoundSummary( + size_t _round, + clock_t _roundTimeStart, + clock_t _totalTimeStart +) const +{ + clock_t now = clock(); + double roundTime = static_cast(now - _roundTimeStart) / CLOCKS_PER_SEC; + double totalTime = static_cast(now - _totalTimeStart) / CLOCKS_PER_SEC; + + if (!m_options.showOnlyTopChromosome) + { + if (m_options.showRoundInfo) + { + m_outputStream << "---------- ROUND " << _round + 1; + m_outputStream << " [round: " << fixed << setprecision(1) << roundTime << " s,"; + m_outputStream << " total: " << fixed << setprecision(1) << totalTime << " s]"; + m_outputStream << " ----------" << endl; + } + else if (m_population.individuals().size() > 0) + m_outputStream << endl; + + m_outputStream << m_population; + } + else if (m_population.individuals().size() > 0) + { + if (m_options.showRoundInfo) + { + m_outputStream << setw(5) << _round + 1 << " | "; + m_outputStream << setw(5) << fixed << setprecision(1) << totalTime << " | "; + } + + m_outputStream << m_population.individuals()[0] << endl; + } +} + +void AlgorithmRunner::printInitialPopulation() const +{ + if (!m_options.showInitialPopulation) + return; + + m_outputStream << "---------- INITIAL POPULATION ----------" << endl; + m_outputStream << m_population; +} + +void AlgorithmRunner::printCacheStats() const +{ + if (!m_options.showCacheStats) + return; + + CacheStats totalStats{}; + size_t disabledCacheCount = 0; + for (size_t i = 0; i < m_programCaches.size(); ++i) + if (m_programCaches[i] != nullptr) + totalStats += m_programCaches[i]->gatherStats(); + else + ++disabledCacheCount; + + m_outputStream << "---------- CACHE STATS ----------" << endl; + + if (disabledCacheCount < m_programCaches.size()) + { + for (auto& [round, count]: totalStats.roundEntryCounts) + m_outputStream << "Round " << round << ": " << count << " entries" << endl; + m_outputStream << "Total hits: " << totalStats.hits << endl; + m_outputStream << "Total misses: " << totalStats.misses << endl; + m_outputStream << "Size of cached code: " << totalStats.totalCodeSize << endl; + } + + if (disabledCacheCount == m_programCaches.size()) + m_outputStream << "Program cache disabled" << endl; + else if (disabledCacheCount > 0) + { + m_outputStream << "Program cache disabled for " << disabledCacheCount << " out of "; + m_outputStream << m_programCaches.size() << " programs" << endl; + } +} + +void AlgorithmRunner::populationAutosave() const +{ + if (!m_options.populationAutosaveFile.has_value()) + return; + + ofstream outputStream(m_options.populationAutosaveFile.value(), ios::out | ios::trunc); + assertThrow( + outputStream.is_open(), + FileOpenError, + "Could not open file '" + m_options.populationAutosaveFile.value() + "': " + strerror(errno) + ); + + for (auto& individual: m_population.individuals()) + outputStream << individual.chromosome << endl; + + assertThrow( + !outputStream.bad(), + FileWriteError, + "Error while writing to file '" + m_options.populationAutosaveFile.value() + "': " + strerror(errno) + ); +} + +void AlgorithmRunner::cacheClear() +{ + for (auto& cache: m_programCaches) + if (cache != nullptr) + cache->clear(); +} + +void AlgorithmRunner::cacheStartRound(size_t _roundNumber) +{ + for (auto& cache: m_programCaches) + if (cache != nullptr) + cache->startRound(_roundNumber); +} + +void AlgorithmRunner::randomiseDuplicates() +{ + if (m_options.randomiseDuplicates) + { + assert(m_options.minChromosomeLength.has_value()); + assert(m_options.maxChromosomeLength.has_value()); + + m_population = randomiseDuplicates( + m_population, + m_options.minChromosomeLength.value(), + m_options.maxChromosomeLength.value() + ); + } +} + +Population AlgorithmRunner::randomiseDuplicates( + Population _population, + size_t _minChromosomeLength, + size_t _maxChromosomeLength +) +{ + if (_population.individuals().size() == 0) + return _population; + + vector chromosomes{_population.individuals()[0].chromosome}; + size_t duplicateCount = 0; + for (size_t i = 1; i < _population.individuals().size(); ++i) + if (_population.individuals()[i].chromosome == _population.individuals()[i - 1].chromosome) + ++duplicateCount; + else + chromosomes.push_back(_population.individuals()[i].chromosome); + + return ( + Population(_population.fitnessMetric(), chromosomes) + + Population::makeRandom(_population.fitnessMetric(), duplicateCount, _minChromosomeLength, _maxChromosomeLength) + ); +} diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h new file mode 100644 index 000000000..4326ce227 --- /dev/null +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -0,0 +1,98 @@ +/* + 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 . +*/ +/** + * Contains the implementation of a class that manages the execution of a genetic algorithm. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * Manages a population and executes a genetic algorithm on it. It's independent of the + * implementation details of a specific algorithm which is pluggable via @a GeneticAlgorithm class. + * + * The class is also responsible for providing text feedback on the execution of the algorithm + * to the associated output stream. + */ +class AlgorithmRunner +{ +public: + struct Options + { + std::optional maxRounds = std::nullopt; + std::optional populationAutosaveFile = std::nullopt; + bool randomiseDuplicates = false; + std::optional minChromosomeLength = std::nullopt; + std::optional maxChromosomeLength = std::nullopt; + bool showInitialPopulation = false; + bool showOnlyTopChromosome = false; + bool showRoundInfo = true; + bool showCacheStats = false; + }; + + AlgorithmRunner( + Population _initialPopulation, + std::vector> _programCaches, + Options _options, + std::ostream& _outputStream + ): + m_population(std::move(_initialPopulation)), + m_programCaches(std::move(_programCaches)), + m_options(std::move(_options)), + m_outputStream(_outputStream) {} + + void run(GeneticAlgorithm& _algorithm); + + Options const& options() const { return m_options; } + Population const& population() const { return m_population; } + +private: + void printRoundSummary( + size_t _round, + clock_t _roundTimeStart, + clock_t _totalTimeStart + ) const; + void printInitialPopulation() const; + void printCacheStats() const; + void populationAutosave() const; + void randomiseDuplicates(); + void cacheClear(); + void cacheStartRound(size_t _roundNumber); + + static Population randomiseDuplicates( + Population _population, + size_t _minChromosomeLength, + size_t _maxChromosomeLength + ); + + Population m_population; + std::vector> m_programCaches; + Options m_options; + std::ostream& m_outputStream; +}; + +} diff --git a/tools/yulPhaser/Common.cpp b/tools/yulPhaser/Common.cpp new file mode 100644 index 000000000..aa7ba85b5 --- /dev/null +++ b/tools/yulPhaser/Common.cpp @@ -0,0 +1,45 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::phaser; + +vector phaser::readLinesFromFile(string const& _path) +{ + ifstream inputStream(_path); + assertThrow(inputStream.is_open(), FileOpenError, "Could not open file '" + _path + "': " + strerror(errno)); + + string line; + vector lines; + while (!getline(inputStream, line).fail()) + lines.push_back(line); + + assertThrow(!inputStream.bad(), FileReadError, "Error while reading from file '" + _path + "': " + strerror(errno)); + + return lines; +} diff --git a/tools/yulPhaser/Common.h b/tools/yulPhaser/Common.h new file mode 100644 index 000000000..c37afa2fb --- /dev/null +++ b/tools/yulPhaser/Common.h @@ -0,0 +1,77 @@ +/* + 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 . +*/ +/** + * Miscellaneous utilities for use in yul-phaser. + */ + +#pragma once + +#include +#include +#include +#include + +namespace solidity::phaser +{ + +/// Loads the whole file into memory, splits the content into lines, strips newlines and +/// returns the result as a list of strings. +/// +/// Throws FileOpenError if the file does not exist or cannot be opened for reading. +/// Throws FileReadError if any read operation fails during the whole process. +std::vector readLinesFromFile(std::string const& _path); + +/// Reads a token from the input stream and translates it to a string using a map. +/// Sets the failbit in the stream if there's no matching value in the map. +template +std::istream& deserializeChoice( + std::istream& _inputStream, + C& _choice, + std::map const& _stringToValueMap +) +{ + std::string deserializedValue; + _inputStream >> deserializedValue; + + auto const& pair = _stringToValueMap.find(deserializedValue); + if (pair != _stringToValueMap.end()) + _choice = pair->second; + else + _inputStream.setstate(std::ios_base::failbit); + + return _inputStream; +} + +/// Translates a value to a string using a map and prints it to the output stream. +/// Sets the failbit if the value is not in the map. +template +std::ostream& serializeChoice( + std::ostream& _outputStream, + C const& _choice, + std::map const& _valueToStringMap +) +{ + auto const& pair = _valueToStringMap.find(_choice); + if (pair != _valueToStringMap.end()) + _outputStream << pair->second; + else + _outputStream.setstate(std::ios_base::failbit); + + return _outputStream; +} + +} diff --git a/tools/yulPhaser/Exceptions.h b/tools/yulPhaser/Exceptions.h index ae75d19ef..6807d7180 100644 --- a/tools/yulPhaser/Exceptions.h +++ b/tools/yulPhaser/Exceptions.h @@ -22,6 +22,13 @@ namespace solidity::phaser { -struct InvalidProgram: virtual util::Exception {}; +struct BadInput: virtual util::Exception {}; +struct InvalidProgram: virtual BadInput {}; +struct NoInputFiles: virtual BadInput {}; +struct MissingFile: virtual BadInput {}; + +struct FileOpenError: virtual util::Exception {}; +struct FileReadError: virtual util::Exception {}; +struct FileWriteError: virtual util::Exception {}; } diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index be7ea5497..5279cea72 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -17,14 +17,102 @@ #include +#include + +#include + using namespace std; +using namespace solidity::util; using namespace solidity::phaser; -size_t ProgramSize::evaluate(Chromosome const& _chromosome) const +Program const& ProgramBasedMetric::program() const { - Program programCopy = m_program; + if (m_programCache == nullptr) + return m_program.value(); + else + return m_programCache->program(); +} + +Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) +{ + if (m_programCache == nullptr) + return optimisedProgramNoCache(_chromosome); + + return m_programCache->optimiseProgram( + toString(_chromosome), + m_repetitionCount + ); +} + +Program ProgramBasedMetric::optimisedProgramNoCache(Chromosome const& _chromosome) const +{ + Program programCopy = program(); for (size_t i = 0; i < m_repetitionCount; ++i) programCopy.optimise(_chromosome.optimisationSteps()); - return programCopy.codeSize(); + return programCopy; +} + +size_t ProgramSize::evaluate(Chromosome const& _chromosome) +{ + return optimisedProgram(_chromosome).codeSize(); +} + +size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome) +{ + size_t const scalingFactor = pow(10, m_fixedPointPrecision); + + size_t unoptimisedSize = optimisedProgram(Chromosome("")).codeSize(); + if (unoptimisedSize == 0) + return scalingFactor; + + size_t optimisedSize = optimisedProgram(_chromosome).codeSize(); + + return static_cast(round( + static_cast(optimisedSize) / unoptimisedSize * scalingFactor + )); +} + +size_t FitnessMetricAverage::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t total = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + total += m_metrics[i]->evaluate(_chromosome); + + return total / m_metrics.size(); +} + +size_t FitnessMetricSum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t total = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + total += m_metrics[i]->evaluate(_chromosome); + + return total; +} + +size_t FitnessMetricMaximum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t maximum = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + maximum = max(maximum, m_metrics[i]->evaluate(_chromosome)); + + return maximum; +} + +size_t FitnessMetricMinimum::evaluate(Chromosome const& _chromosome) +{ + assert(m_metrics.size() > 0); + + size_t minimum = m_metrics[0]->evaluate(_chromosome); + for (size_t i = 1; i < m_metrics.size(); ++i) + minimum = min(minimum, m_metrics[i]->evaluate(_chromosome)); + + return minimum; } diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index 7fac5f080..72e811152 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -22,8 +22,10 @@ #include #include +#include #include +#include namespace solidity::phaser { @@ -43,25 +45,139 @@ public: FitnessMetric& operator=(FitnessMetric const&) = delete; virtual ~FitnessMetric() = default; - virtual size_t evaluate(Chromosome const& _chromosome) const = 0; + virtual size_t evaluate(Chromosome const& _chromosome) = 0; +}; + +/** + * Abstract base class for fitness metrics that return values based on program size. + * + * The class provides utilities for optimising programs according to the information stored in + * chromosomes. Allows using @a ProgramCache. + * + * It can also store weights for the @a CodeSize metric. It does not do anything with + * them because it does not actually compute the code size but they are readily available for use + * by derived classes. + */ +class ProgramBasedMetric: public FitnessMetric +{ +public: + explicit ProgramBasedMetric( + std::optional _program, + std::shared_ptr _programCache, + size_t _repetitionCount = 1 + ): + m_program(std::move(_program)), + m_programCache(std::move(_programCache)), + m_repetitionCount(_repetitionCount) + { + assert(m_program.has_value() == (m_programCache == nullptr)); + } + + Program const& program() const; + ProgramCache const* programCache() const { return m_programCache.get(); } + size_t repetitionCount() const { return m_repetitionCount; } + + Program optimisedProgram(Chromosome const& _chromosome); + Program optimisedProgramNoCache(Chromosome const& _chromosome) const; + +private: + std::optional m_program; + std::shared_ptr m_programCache; + size_t m_repetitionCount; }; /** * Fitness metric based on the size of a specific program after applying the optimisations from the * chromosome to it. */ -class ProgramSize: public FitnessMetric +class ProgramSize: public ProgramBasedMetric { public: - explicit ProgramSize(Program _program, size_t _repetitionCount = 1): - m_program(std::move(_program)), - m_repetitionCount(_repetitionCount) {} + using ProgramBasedMetric::ProgramBasedMetric; + size_t evaluate(Chromosome const& _chromosome) override; +}; - size_t evaluate(Chromosome const& _chromosome) const override; +/** + * Fitness metric based on the size of a specific program after applying the optimisations from the + * chromosome to it in relation to the original, unoptimised program. + * + * Since metric values are integers, the class multiplies the ratio by 10^@a _fixedPointPrecision + * before rounding it. + */ +class RelativeProgramSize: public ProgramBasedMetric +{ +public: + explicit RelativeProgramSize( + std::optional _program, + std::shared_ptr _programCache, + size_t _fixedPointPrecision, + size_t _repetitionCount = 1 + ): + ProgramBasedMetric(std::move(_program), std::move(_programCache), _repetitionCount), + m_fixedPointPrecision(_fixedPointPrecision) {} + + size_t fixedPointPrecision() const { return m_fixedPointPrecision; } + + size_t evaluate(Chromosome const& _chromosome) override; private: - Program m_program; - size_t m_repetitionCount; + size_t m_fixedPointPrecision; +}; + +/** + * Abstract base class for fitness metrics that compute their value based on values of multiple + * other, nested metrics. + */ +class FitnessMetricCombination: public FitnessMetric +{ +public: + explicit FitnessMetricCombination(std::vector> _metrics): + m_metrics(std::move(_metrics)) {} + + std::vector> const& metrics() const { return m_metrics; } + +protected: + std::vector> m_metrics; +}; + +/** + * Fitness metric that returns the average of values of its nested metrics. + */ +class FitnessMetricAverage: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the sum of values of its nested metrics. + */ +class FitnessMetricSum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the highest of values of its nested metrics. + */ +class FitnessMetricMaximum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; +}; + +/** + * Fitness metric that returns the lowest of values of its nested metrics. + */ +class FitnessMetricMinimum: public FitnessMetricCombination +{ +public: + using FitnessMetricCombination::FitnessMetricCombination; + size_t evaluate(Chromosome const& _chromosome) override; }; } diff --git a/tools/yulPhaser/GeneticAlgorithms.cpp b/tools/yulPhaser/GeneticAlgorithms.cpp index 756a410f7..432f3fe38 100644 --- a/tools/yulPhaser/GeneticAlgorithms.cpp +++ b/tools/yulPhaser/GeneticAlgorithms.cpp @@ -16,35 +16,51 @@ */ #include +#include #include +#include using namespace std; using namespace solidity::phaser; -void GeneticAlgorithm::run(optional _numRounds) -{ - for (size_t round = 0; !_numRounds.has_value() || round < _numRounds.value(); ++round) - { - runNextRound(); - - m_outputStream << "---------- ROUND " << round << " ----------" << endl; - m_outputStream << m_population; - } -} - -void RandomAlgorithm::runNextRound() +Population RandomAlgorithm::runNextRound(Population _population) { RangeSelection elite(0.0, m_options.elitePoolSize); - Population elitePopulation = m_population.select(elite); - size_t replacementCount = m_population.individuals().size() - elitePopulation.individuals().size(); + Population elitePopulation = _population.select(elite); + size_t replacementCount = _population.individuals().size() - elitePopulation.individuals().size(); - m_population = + return move(elitePopulation) + Population::makeRandom( - m_population.fitnessMetric(), + _population.fitnessMetric(), replacementCount, m_options.minChromosomeLength, m_options.maxChromosomeLength ); } + +Population GenerationalElitistWithExclusivePools::runNextRound(Population _population) +{ + double elitePoolSize = 1.0 - (m_options.mutationPoolSize + m_options.crossoverPoolSize); + RangeSelection elite(0.0, elitePoolSize); + + return + _population.select(elite) + + _population.select(elite).mutate( + RandomSelection(m_options.mutationPoolSize / elitePoolSize), + alternativeMutations( + m_options.randomisationChance, + geneRandomisation(m_options.percentGenesToRandomise), + alternativeMutations( + m_options.deletionVsAdditionChance, + geneDeletion(m_options.percentGenesToAddOrDelete), + geneAddition(m_options.percentGenesToAddOrDelete) + ) + ) + ) + + _population.select(elite).crossover( + RandomPairSelection(m_options.crossoverPoolSize / elitePoolSize), + randomPointCrossover() + ); +} diff --git a/tools/yulPhaser/GeneticAlgorithms.h b/tools/yulPhaser/GeneticAlgorithms.h index a7600d894..a2a7484d6 100644 --- a/tools/yulPhaser/GeneticAlgorithms.h +++ b/tools/yulPhaser/GeneticAlgorithms.h @@ -22,45 +22,25 @@ #include -#include -#include - namespace solidity::phaser { /** * Abstract base class for genetic algorithms. - * - * The main feature is the @a run() method that executes the algorithm, updating the internal - * population during each round and printing the results to the stream provided to the constructor. - * - * Derived classes can provide specific methods for updating the population by implementing - * the @a runNextRound() method. + * The main feature is the @a runNextRound() method that executes one round of the algorithm, + * on the supplied population. */ class GeneticAlgorithm { public: - GeneticAlgorithm(Population _initialPopulation, std::ostream& _outputStream): - m_population(std::move(_initialPopulation)), - m_outputStream(_outputStream) {} - + GeneticAlgorithm() {} GeneticAlgorithm(GeneticAlgorithm const&) = delete; GeneticAlgorithm& operator=(GeneticAlgorithm const&) = delete; virtual ~GeneticAlgorithm() = default; - Population const& population() const { return m_population; } - - void run(std::optional _numRounds = std::nullopt); - /// The method that actually implements the algorithm. Should use @a m_population as input and /// replace it with the updated state after the round. - virtual void runNextRound() = 0; - -protected: - Population m_population; - -private: - std::ostream& m_outputStream; + virtual Population runNextRound(Population _population) = 0; }; /** @@ -95,18 +75,65 @@ public: } }; - explicit RandomAlgorithm( - Population _initialPopulation, - std::ostream& _outputStream, - Options const& _options - ): - GeneticAlgorithm(_initialPopulation, _outputStream), + explicit RandomAlgorithm(Options const& _options): m_options(_options) { assert(_options.isValid()); } - void runNextRound() override; + Options const& options() const { return m_options; } + + Population runNextRound(Population _population) override; + +private: + Options m_options; +}; + +/** + * A generational, elitist genetic algorithm that replaces the population by mutating and crossing + * over chromosomes from the elite. + * + * The elite consists of individuals not included in the crossover and mutation pools. + * The crossover operator used is @a randomPointCrossover. The mutation operator is randomly chosen + * from three possibilities: @a geneRandomisation, @a geneDeletion or @a geneAddition (with + * configurable probabilities). Each mutation also has a parameter determining the chance of a gene + * being affected by it. + */ +class GenerationalElitistWithExclusivePools: public GeneticAlgorithm +{ +public: + struct Options + { + double mutationPoolSize; ///< Percentage of population to regenerate using mutations in each round. + double crossoverPoolSize; ///< Percentage of population to regenerate using crossover in each round. + double randomisationChance; ///< The chance of choosing @a geneRandomisation as the mutation to perform + double deletionVsAdditionChance; ///< The chance of choosing @a geneDeletion as the mutation if randomisation was not chosen. + double percentGenesToRandomise; ///< The chance of any given gene being mutated in gene randomisation. + double percentGenesToAddOrDelete; ///< The chance of a gene being added (or deleted) in gene addition (or deletion). + + bool isValid() const + { + return ( + 0 <= mutationPoolSize && mutationPoolSize <= 1.0 && + 0 <= crossoverPoolSize && crossoverPoolSize <= 1.0 && + 0 <= randomisationChance && randomisationChance <= 1.0 && + 0 <= deletionVsAdditionChance && deletionVsAdditionChance <= 1.0 && + 0 <= percentGenesToRandomise && percentGenesToRandomise <= 1.0 && + 0 <= percentGenesToAddOrDelete && percentGenesToAddOrDelete <= 1.0 && + mutationPoolSize + crossoverPoolSize <= 1.0 + ); + } + }; + + GenerationalElitistWithExclusivePools(Options const& _options): + m_options(_options) + { + assert(_options.isValid()); + } + + Options const& options() const { return m_options; } + + Population runNextRound(Population _population) override; private: Options m_options; diff --git a/tools/yulPhaser/Mutations.cpp b/tools/yulPhaser/Mutations.cpp new file mode 100644 index 000000000..86f815198 --- /dev/null +++ b/tools/yulPhaser/Mutations.cpp @@ -0,0 +1,147 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::phaser; + +function phaser::geneRandomisation(double _chance) +{ + return [=](Chromosome const& _chromosome) + { + vector optimisationSteps; + for (auto const& step: _chromosome.optimisationSteps()) + optimisationSteps.push_back( + SimulationRNG::bernoulliTrial(_chance) ? + Chromosome::randomOptimisationStep() : + step + ); + + return Chromosome(move(optimisationSteps)); + }; +} + +function phaser::geneDeletion(double _chance) +{ + return [=](Chromosome const& _chromosome) + { + vector optimisationSteps; + for (auto const& step: _chromosome.optimisationSteps()) + if (!SimulationRNG::bernoulliTrial(_chance)) + optimisationSteps.push_back(step); + + return Chromosome(move(optimisationSteps)); + }; +} + +function phaser::geneAddition(double _chance) +{ + return [=](Chromosome const& _chromosome) + { + vector optimisationSteps; + + if (SimulationRNG::bernoulliTrial(_chance)) + optimisationSteps.push_back(Chromosome::randomOptimisationStep()); + + for (auto const& step: _chromosome.optimisationSteps()) + { + optimisationSteps.push_back(step); + if (SimulationRNG::bernoulliTrial(_chance)) + optimisationSteps.push_back(Chromosome::randomOptimisationStep()); + } + + return Chromosome(move(optimisationSteps)); + }; +} + +function phaser::alternativeMutations( + double _firstMutationChance, + function _mutation1, + function _mutation2 +) +{ + return [=](Chromosome const& _chromosome) + { + if (SimulationRNG::bernoulliTrial(_firstMutationChance)) + return _mutation1(_chromosome); + else + return _mutation2(_chromosome); + }; +} + +namespace +{ + +Chromosome buildChromosomesBySwappingParts( + Chromosome const& _chromosome1, + Chromosome const& _chromosome2, + size_t _crossoverPoint +) +{ + assert(_crossoverPoint <= _chromosome1.length()); + assert(_crossoverPoint <= _chromosome2.length()); + + auto begin1 = _chromosome1.optimisationSteps().begin(); + auto begin2 = _chromosome2.optimisationSteps().begin(); + + return Chromosome( + vector(begin1, begin1 + _crossoverPoint) + + vector(begin2 + _crossoverPoint, _chromosome2.optimisationSteps().end()) + ); +} + +} + +function phaser::randomPointCrossover() +{ + return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2) + { + size_t minLength = min(_chromosome1.length(), _chromosome2.length()); + + // Don't use position 0 (because this just swaps the values) unless it's the only choice. + size_t minPoint = (minLength > 0? 1 : 0); + assert(minPoint <= minLength); + + size_t randomPoint = SimulationRNG::uniformInt(minPoint, minLength); + return buildChromosomesBySwappingParts(_chromosome1, _chromosome2, randomPoint); + }; +} + +function phaser::fixedPointCrossover(double _crossoverPoint) +{ + assert(0.0 <= _crossoverPoint && _crossoverPoint <= 1.0); + + return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2) + { + size_t minLength = min(_chromosome1.length(), _chromosome2.length()); + size_t concretePoint = static_cast(round(minLength * _crossoverPoint)); + + return buildChromosomesBySwappingParts(_chromosome1, _chromosome2, concretePoint); + }; +} diff --git a/tools/yulPhaser/Mutations.h b/tools/yulPhaser/Mutations.h new file mode 100644 index 000000000..bff48c52b --- /dev/null +++ b/tools/yulPhaser/Mutations.h @@ -0,0 +1,73 @@ +/* + 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 . +*/ +/** + * Mutation and crossover operators for use in genetic algorithms. + */ + +#pragma once + +#include + +#include +#include + +namespace solidity::phaser +{ + +using Mutation = Chromosome(Chromosome const&); +using Crossover = Chromosome(Chromosome const&, Chromosome const&); + +// MUTATIONS + +/// Creates a mutation operator that iterates over all genes in a chromosome and with probability +/// @a _chance replaces a gene with a random one (which could also be the same as the original). +std::function geneRandomisation(double _chance); + +/// Creates a mutation operator that iterates over all genes in a chromosome and with probability +/// @a _chance deletes it. +std::function geneDeletion(double _chance); + +/// Creates a mutation operator that iterates over all positions in a chromosome (including spots +/// at the beginning and at the end of the sequence) and with probability @a _chance insert a new, +/// randomly chosen gene. +std::function geneAddition(double _chance); + +/// Creates a mutation operator that always applies one of the mutations passed to it. +/// The probability that the chosen mutation is the first one is @a _firstMutationChance. +/// randomly chosen gene. +std::function alternativeMutations( + double _firstMutationChance, + std::function _mutation1, + std::function _mutation2 +); + +// CROSSOVER + +/// Creates a crossover operator that randomly selects a number between 0 and 1 and uses it as the +/// position at which to perform perform @a fixedPointCrossover. +std::function randomPointCrossover(); + +/// Creates a crossover operator that always chooses a point that lies at @a _crossoverPoint +/// percent of the length of the shorter chromosome. Then creates a new chromosome by +/// splitting both inputs at the crossover point and stitching output from the first half or first +/// input and the second half of the second input. +/// +/// Avoids selecting position 0 (since this just produces a chromosome identical to the second one) +/// unless there is no other choice (i.e. one of the chromosomes is empty). +std::function fixedPointCrossover(double _crossoverPoint); + +} diff --git a/tools/yulPhaser/PairSelections.cpp b/tools/yulPhaser/PairSelections.cpp new file mode 100644 index 000000000..8f3fd0f7f --- /dev/null +++ b/tools/yulPhaser/PairSelections.cpp @@ -0,0 +1,65 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include + +using namespace std; +using namespace solidity::phaser; + +vector> RandomPairSelection::materialise(size_t _poolSize) const +{ + if (_poolSize < 2) + return {}; + + size_t count = static_cast(round(_poolSize * m_selectionSize)); + + vector> selection; + for (size_t i = 0; i < count; ++i) + { + size_t index1 = SimulationRNG::uniformInt(0, _poolSize - 1); + size_t index2; + do + { + index2 = SimulationRNG::uniformInt(0, _poolSize - 1); + } while (index1 == index2); + + selection.push_back({index1, index2}); + } + + return selection; +} + +vector> PairMosaicSelection::materialise(size_t _poolSize) const +{ + if (_poolSize < 2) + return {}; + + size_t count = static_cast(round(_poolSize * m_selectionSize)); + + vector> selection; + for (size_t i = 0; i < count; ++i) + { + tuple pair = m_pattern[i % m_pattern.size()]; + selection.push_back({min(get<0>(pair), _poolSize - 1), min(get<1>(pair), _poolSize - 1)}); + } + + return selection; +} diff --git a/tools/yulPhaser/PairSelections.h b/tools/yulPhaser/PairSelections.h new file mode 100644 index 000000000..7778d6567 --- /dev/null +++ b/tools/yulPhaser/PairSelections.h @@ -0,0 +1,99 @@ +/* + 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 . +*/ +/** + * Contains an abstract base class representing a selection of pairs of elements from a collection + * and its concrete implementations. + */ + +#pragma once + +#include +#include +#include + +namespace solidity::phaser +{ + +/** + * Abstract base class for selections of pairs elements from a collection. + * + * An instance of this class represents a specific method of selecting a set of pairs of elements + * from containers of arbitrary sizes. The selected pairs always point at a subset of the elements + * from the container but may indicate the same element more than once. The pairs themselves can + * repeat too. The selection may or may not be fixed - it's up to a specific implementation whether + * subsequent calls for the same container produce the same indices or not. + * + * Derived classes are meant to override the @a materialise() method. + * This method is expected to produce pairs of selected elements given the size of the collection. + */ +class PairSelection +{ +public: + PairSelection() = default; + PairSelection(PairSelection const&) = delete; + PairSelection& operator=(PairSelection const&) = delete; + virtual ~PairSelection() = default; + + virtual std::vector> materialise(size_t _poolSize) const = 0; +}; + +/** + * A selection that selects pairs of random elements from a container. The resulting set of pairs + * may contain the same pair more than once but does not contain pairs of duplicates. Always + * selects as many pairs as the size of the container multiplied by @a _selectionSize (unless the + * container is empty). + */ +class RandomPairSelection: public PairSelection +{ +public: + explicit RandomPairSelection(double _selectionSize): + m_selectionSize(_selectionSize) {} + + std::vector> materialise(size_t _poolSize) const override; + +private: + double m_selectionSize; +}; + +/** + * A selection that selects pairs of elements at specific, fixed positions indicated by a repeating + * "pattern". If the positions in the pattern exceed the size of the container, they are capped at + * the maximum available position. Always selects as many pairs as the size of the container + * multiplied by @a _selectionSize (unless the container is empty). + * + * E.g. if the pattern is {{0, 1}, {3, 9}} and collection size is 5, the selection will materialise + * into {{0, 1}, {3, 4}, {0, 1}, {3, 4}, {0, 1}}. If the size is 3, it will be + * {{0, 1}, {2, 2}, {0, 1}}. + */ +class PairMosaicSelection: public PairSelection +{ +public: + explicit PairMosaicSelection(std::vector> _pattern, double _selectionSize = 1.0): + m_pattern(move(_pattern)), + m_selectionSize(_selectionSize) + { + assert(m_pattern.size() > 0 || _selectionSize == 0.0); + } + + std::vector> materialise(size_t _poolSize) const override; + +private: + std::vector> m_pattern; + double m_selectionSize; +}; + +} diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp new file mode 100644 index 000000000..2e38edbf0 --- /dev/null +++ b/tools/yulPhaser/Phaser.cpp @@ -0,0 +1,718 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::phaser; + +namespace po = boost::program_options; + +namespace +{ + +map const PhaserModeToStringMap = +{ + {PhaserMode::RunAlgorithm, "run-algorithm"}, + {PhaserMode::PrintOptimisedPrograms, "print-optimised-programs"}, + {PhaserMode::PrintOptimisedASTs, "print-optimised-asts"}, +}; +map const StringToPhaserModeMap = invertMap(PhaserModeToStringMap); + +map const AlgorithmToStringMap = +{ + {Algorithm::Random, "random"}, + {Algorithm::GEWEP, "GEWEP"}, +}; +map const StringToAlgorithmMap = invertMap(AlgorithmToStringMap); + +map MetricChoiceToStringMap = +{ + {MetricChoice::CodeSize, "code-size"}, + {MetricChoice::RelativeCodeSize, "relative-code-size"}, +}; +map const StringToMetricChoiceMap = invertMap(MetricChoiceToStringMap); + +map const MetricAggregatorChoiceToStringMap = +{ + {MetricAggregatorChoice::Average, "average"}, + {MetricAggregatorChoice::Sum, "sum"}, + {MetricAggregatorChoice::Maximum, "maximum"}, + {MetricAggregatorChoice::Minimum, "minimum"}, +}; +map const StringToMetricAggregatorChoiceMap = invertMap(MetricAggregatorChoiceToStringMap); + +} + +istream& phaser::operator>>(istream& _inputStream, PhaserMode& _phaserMode) { return deserializeChoice(_inputStream, _phaserMode, StringToPhaserModeMap); } +ostream& phaser::operator<<(ostream& _outputStream, PhaserMode _phaserMode) { return serializeChoice(_outputStream, _phaserMode, PhaserModeToStringMap); } +istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); } +ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); } +istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); } +ostream& phaser::operator<<(ostream& _outputStream, MetricChoice _metric) { return serializeChoice(_outputStream, _metric, MetricChoiceToStringMap); } +istream& phaser::operator>>(istream& _inputStream, MetricAggregatorChoice& _aggregator) { return deserializeChoice(_inputStream, _aggregator, StringToMetricAggregatorChoiceMap); } +ostream& phaser::operator<<(ostream& _outputStream, MetricAggregatorChoice _aggregator) { return serializeChoice(_outputStream, _aggregator, MetricAggregatorChoiceToStringMap); } + +GeneticAlgorithmFactory::Options GeneticAlgorithmFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["algorithm"].as(), + _arguments["min-chromosome-length"].as(), + _arguments["max-chromosome-length"].as(), + _arguments.count("random-elite-pool-size") > 0 ? + _arguments["random-elite-pool-size"].as() : + optional{}, + _arguments["gewep-mutation-pool-size"].as(), + _arguments["gewep-crossover-pool-size"].as(), + _arguments["gewep-randomisation-chance"].as(), + _arguments["gewep-deletion-vs-addition-chance"].as(), + _arguments.count("gewep-genes-to-randomise") > 0 ? + _arguments["gewep-genes-to-randomise"].as() : + optional{}, + _arguments.count("gewep-genes-to-add-or-delete") > 0 ? + _arguments["gewep-genes-to-add-or-delete"].as() : + optional{}, + }; +} + +unique_ptr GeneticAlgorithmFactory::build( + Options const& _options, + size_t _populationSize +) +{ + assert(_populationSize > 0); + + switch (_options.algorithm) + { + case Algorithm::Random: + { + double elitePoolSize = 1.0 / _populationSize; + + if (_options.randomElitePoolSize.has_value()) + elitePoolSize = _options.randomElitePoolSize.value(); + + return make_unique(RandomAlgorithm::Options{ + /* elitePoolSize = */ elitePoolSize, + /* minChromosomeLength = */ _options.minChromosomeLength, + /* maxChromosomeLength = */ _options.maxChromosomeLength, + }); + } + case Algorithm::GEWEP: + { + double percentGenesToRandomise = 1.0 / _options.maxChromosomeLength; + double percentGenesToAddOrDelete = percentGenesToRandomise; + + if (_options.gewepGenesToRandomise.has_value()) + percentGenesToRandomise = _options.gewepGenesToRandomise.value(); + if (_options.gewepGenesToAddOrDelete.has_value()) + percentGenesToAddOrDelete = _options.gewepGenesToAddOrDelete.value(); + + return make_unique(GenerationalElitistWithExclusivePools::Options{ + /* mutationPoolSize = */ _options.gewepMutationPoolSize, + /* crossoverPoolSize = */ _options.gewepCrossoverPoolSize, + /* randomisationChance = */ _options.gewepRandomisationChance, + /* deletionVsAdditionChance = */ _options.gewepDeletionVsAdditionChance, + /* percentGenesToRandomise = */ percentGenesToRandomise, + /* percentGenesToAddOrDelete = */ percentGenesToAddOrDelete, + }); + } + default: + assertThrow(false, solidity::util::Exception, "Invalid Algorithm value."); + } +} + +FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["metric"].as(), + _arguments["metric-aggregator"].as(), + _arguments["relative-metric-scale"].as(), + _arguments["chromosome-repetitions"].as(), + }; +} + +unique_ptr FitnessMetricFactory::build( + Options const& _options, + vector _programs, + vector> _programCaches +) +{ + assert(_programCaches.size() == _programs.size()); + assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files."); + + vector> metrics; + switch (_options.metric) + { + case MetricChoice::CodeSize: + { + for (size_t i = 0; i < _programs.size(); ++i) + metrics.push_back(make_unique( + _programCaches[i] != nullptr ? optional{} : move(_programs[i]), + move(_programCaches[i]), + _options.chromosomeRepetitions + )); + + break; + } + case MetricChoice::RelativeCodeSize: + { + for (size_t i = 0; i < _programs.size(); ++i) + metrics.push_back(make_unique( + _programCaches[i] != nullptr ? optional{} : move(_programs[i]), + move(_programCaches[i]), + _options.relativeMetricScale, + _options.chromosomeRepetitions + )); + break; + } + default: + assertThrow(false, solidity::util::Exception, "Invalid MetricChoice value."); + } + + switch (_options.metricAggregator) + { + case MetricAggregatorChoice::Average: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Sum: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Maximum: + return make_unique(move(metrics)); + case MetricAggregatorChoice::Minimum: + return make_unique(move(metrics)); + default: + assertThrow(false, solidity::util::Exception, "Invalid MetricAggregatorChoice value."); + } +} + +PopulationFactory::Options PopulationFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["min-chromosome-length"].as(), + _arguments["max-chromosome-length"].as(), + _arguments.count("population") > 0 ? + _arguments["population"].as>() : + vector{}, + _arguments.count("random-population") > 0 ? + _arguments["random-population"].as>() : + vector{}, + _arguments.count("population-from-file") > 0 ? + _arguments["population-from-file"].as>() : + vector{}, + }; +} + +Population PopulationFactory::build( + Options const& _options, + shared_ptr _fitnessMetric +) +{ + Population population = buildFromStrings(_options.population, _fitnessMetric); + + size_t combinedSize = 0; + for (size_t populationSize: _options.randomPopulation) + combinedSize += populationSize; + + population = move(population) + buildRandom( + combinedSize, + _options.minChromosomeLength, + _options.maxChromosomeLength, + _fitnessMetric + ); + + for (string const& populationFilePath: _options.populationFromFile) + population = move(population) + buildFromFile(populationFilePath, _fitnessMetric); + + return population; +} + +Population PopulationFactory::buildFromStrings( + vector const& _geneSequences, + shared_ptr _fitnessMetric +) +{ + vector chromosomes; + for (string const& geneSequence: _geneSequences) + chromosomes.emplace_back(geneSequence); + + return Population(move(_fitnessMetric), move(chromosomes)); +} + +Population PopulationFactory::buildRandom( + size_t _populationSize, + size_t _minChromosomeLength, + size_t _maxChromosomeLength, + shared_ptr _fitnessMetric +) +{ + return Population::makeRandom( + move(_fitnessMetric), + _populationSize, + _minChromosomeLength, + _maxChromosomeLength + ); +} + +Population PopulationFactory::buildFromFile( + string const& _filePath, + shared_ptr _fitnessMetric +) +{ + return buildFromStrings(readLinesFromFile(_filePath), move(_fitnessMetric)); +} + +ProgramCacheFactory::Options ProgramCacheFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["program-cache"].as(), + }; +} + +vector> ProgramCacheFactory::build( + Options const& _options, + vector _programs +) +{ + vector> programCaches; + for (Program& program: _programs) + programCaches.push_back(_options.programCacheEnabled ? make_shared(move(program)) : nullptr); + + return programCaches; +} + +ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["input-files"].as>(), + _arguments["prefix"].as(), + }; +} + +vector ProgramFactory::build(Options const& _options) +{ + vector inputPrograms; + for (auto& path: _options.inputFiles) + { + CharStream sourceCode = loadSource(path); + variant programOrErrors = Program::load(sourceCode); + if (holds_alternative(programOrErrors)) + { + cerr << get(programOrErrors) << endl; + assertThrow(false, InvalidProgram, "Failed to load program " + path); + } + + get(programOrErrors).optimise(Chromosome(_options.prefix).optimisationSteps()); + inputPrograms.push_back(move(get(programOrErrors))); + } + + return inputPrograms; +} + +CharStream ProgramFactory::loadSource(string const& _sourcePath) +{ + assertThrow(boost::filesystem::exists(_sourcePath), MissingFile, "Source file does not exist: " + _sourcePath); + + string sourceCode = readFileAsString(_sourcePath); + return CharStream(sourceCode, _sourcePath); +} + +void Phaser::main(int _argc, char** _argv) +{ + optional arguments = parseCommandLine(_argc, _argv); + if (!arguments.has_value()) + return; + + initialiseRNG(arguments.value()); + + runPhaser(arguments.value()); +} + +Phaser::CommandLineDescription Phaser::buildCommandLineDescription() +{ + size_t const lineLength = po::options_description::m_default_line_length; + size_t const minDescriptionLength = lineLength - 23; + + po::options_description keywordDescription( + "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" + "\n" + "Usage: yul-phaser [options] \n" + "Reads as Yul code and tries to find the best order in which to run optimisation" + " phases using a genetic algorithm.\n" + "Example:\n" + "yul-phaser program.yul\n" + "\n" + "Allowed options", + lineLength, + minDescriptionLength + ); + + po::options_description generalDescription("GENERAL", lineLength, minDescriptionLength); + generalDescription.add_options() + ("help", "Show help message and exit.") + ("input-files", po::value>()->required()->value_name(""), "Input files.") + ( + "prefix", + po::value()->value_name("")->default_value(""), + "Initial optimisation steps automatically applied to every input program.\n" + "The result is treated as if it was the actual input, i.e. the steps are not considered " + "a part of the chromosomes and cannot be mutated. The values of relative metric values " + "are also relative to the fitness of a program with these steps applied rather than the " + "fitness of the original program.\n" + "Note that phaser always adds a 'hgo' prefix to ensure that chromosomes can " + "contain arbitrary optimisation steps. This implicit prefix cannot be changed or " + "or removed using this option. The value given here is applied after it." + ) + ("seed", po::value()->value_name(""), "Seed for the random number generator.") + ( + "rounds", + po::value()->value_name(""), + "The number of rounds after which the algorithm should stop. (default=no limit)." + ) + ( + "mode", + po::value()->value_name("")->default_value(PhaserMode::RunAlgorithm), + "Mode of operation. The default is to run the algorithm but you can also tell phaser " + "to do something else with its parameters, e.g. just print the optimised programs and exit." + ) + ; + keywordDescription.add(generalDescription); + + po::options_description algorithmDescription("ALGORITHM", lineLength, minDescriptionLength); + algorithmDescription.add_options() + ( + "algorithm", + po::value()->value_name("")->default_value(Algorithm::GEWEP), + "Algorithm" + ) + ( + "no-randomise-duplicates", + po::bool_switch(), + "By default, after each round of the algorithm duplicate chromosomes are removed from" + "the population and replaced with randomly generated ones. " + "This option disables this postprocessing." + ) + ( + "min-chromosome-length", + po::value()->value_name("")->default_value(12), + "Minimum length of randomly generated chromosomes." + ) + ( + "max-chromosome-length", + po::value()->value_name("")->default_value(30), + "Maximum length of randomly generated chromosomes." + ) + ; + keywordDescription.add(algorithmDescription); + + po::options_description gewepAlgorithmDescription("GEWEP ALGORITHM", lineLength, minDescriptionLength); + gewepAlgorithmDescription.add_options() + ( + "gewep-mutation-pool-size", + po::value()->value_name("")->default_value(0.25), + "Percentage of population to regenerate using mutations in each round." + ) + ( + "gewep-crossover-pool-size", + po::value()->value_name("")->default_value(0.25), + "Percentage of population to regenerate using crossover in each round." + ) + ( + "gewep-randomisation-chance", + po::value()->value_name("")->default_value(0.9), + "The chance of choosing gene randomisation as the mutation to perform." + ) + ( + "gewep-deletion-vs-addition-chance", + po::value()->value_name("")->default_value(0.5), + "The chance of choosing gene deletion as the mutation if randomisation was not chosen." + ) + ( + "gewep-genes-to-randomise", + po::value()->value_name(""), + "The chance of any given gene being mutated in gene randomisation. " + "(default=1/max-chromosome-length)" + ) + ( + "gewep-genes-to-add-or-delete", + po::value()->value_name(""), + "The chance of a gene being added (or deleted) in gene addition (or deletion). " + "(default=1/max-chromosome-length)" + ) + ; + keywordDescription.add(gewepAlgorithmDescription); + + po::options_description randomAlgorithmDescription("RANDOM ALGORITHM", lineLength, minDescriptionLength); + randomAlgorithmDescription.add_options() + ( + "random-elite-pool-size", + po::value()->value_name(""), + "Percentage of the population preserved in each round. " + "(default=one individual, regardless of population size)" + ) + ; + keywordDescription.add(randomAlgorithmDescription); + + po::options_description populationDescription("POPULATION", lineLength, minDescriptionLength); + populationDescription.add_options() + ( + "population", + po::value>()->multitoken()->value_name(""), + "List of chromosomes to be included in the initial population. " + "You can specify multiple values separated with spaces or invoke the option multiple times " + "and all the values will be included." + ) + ( + "random-population", + po::value>()->value_name(""), + "The number of randomly generated chromosomes to be included in the initial population." + ) + ( + "population-from-file", + po::value>()->value_name(""), + "A text file with a list of chromosomes (one per line) to be included in the initial population." + ) + ( + "population-autosave", + po::value()->value_name(""), + "If specified, the population is saved in the specified file after each round. (default=autosave disabled)" + ) + ; + keywordDescription.add(populationDescription); + + po::options_description metricsDescription("METRICS", lineLength, minDescriptionLength); + metricsDescription.add_options() + ( + "metric", + po::value()->value_name("")->default_value(MetricChoice::RelativeCodeSize), + "Metric used to evaluate the fitness of a chromosome." + ) + ( + "metric-aggregator", + po::value()->value_name("")->default_value(MetricAggregatorChoice::Average), + "Operator used to combine multiple fitness metric obtained by evaluating a chromosome " + "separately for each input program." + ) + ( + "relative-metric-scale", + po::value()->value_name("")->default_value(3), + "Scaling factor for values produced by relative fitness metrics. \n" + "Since all metrics must produce integer values, the fractional part of the result is discarded. " + "To keep the numbers meaningful, a relative metric multiples its values by a scaling factor " + "and this option specifies the exponent of this factor. " + "For example with value of 3 the factor is 10^3 = 1000 and the metric will return " + "500 to represent 0.5, 1000 for 1.0, 2000 for 2.0 and so on. " + "Using a bigger factor allows discerning smaller relative differences between chromosomes " + "but makes the numbers less readable and may also lose precision if the numbers are very large." + ) + ( + "chromosome-repetitions", + po::value()->value_name("")->default_value(1), + "Number of times to repeat the sequence optimisation steps represented by a chromosome." + ) + ; + keywordDescription.add(metricsDescription); + + po::options_description cacheDescription("CACHE", lineLength, minDescriptionLength); + cacheDescription.add_options() + ( + "program-cache", + po::bool_switch(), + "Enables caching of intermediate programs corresponding to chromosome prefixes.\n" + "This speeds up fitness evaluation by a lot but eats tons of memory if the chromosomes are long. " + "Disabled by default since there's currently no way to set an upper limit on memory usage but " + "highly recommended if your computer has enough RAM." + ) + ; + keywordDescription.add(cacheDescription); + + po::options_description outputDescription("OUTPUT", lineLength, minDescriptionLength); + outputDescription.add_options() + ( + "show-initial-population", + po::bool_switch(), + "Print the state of the population before the algorithm starts." + ) + ( + "show-only-top-chromosome", + po::bool_switch(), + "Print only the best chromosome found in each round rather than the whole population." + ) + ( + "hide-round", + po::bool_switch(), + "Hide information about the current round (round number and elapsed time)." + ) + ( + "show-cache-stats", + po::bool_switch(), + "Print information about cache size and effectiveness after each round." + ) + ( + "show-seed", + po::bool_switch(), + "Print the selected random seed." + ) + ; + keywordDescription.add(outputDescription); + + po::positional_options_description positionalDescription; + positionalDescription.add("input-files", -1); + + return {keywordDescription, positionalDescription}; +} + +optional Phaser::parseCommandLine(int _argc, char** _argv) +{ + auto [keywordDescription, positionalDescription] = buildCommandLineDescription(); + + po::variables_map arguments; + po::notify(arguments); + + po::command_line_parser parser(_argc, _argv); + parser.options(keywordDescription).positional(positionalDescription); + po::store(parser.run(), arguments); + + if (arguments.count("help") > 0) + { + cout << keywordDescription << endl; + return nullopt; + } + + if (arguments.count("input-files") == 0) + assertThrow(false, NoInputFiles, "Missing argument: input-files."); + + return arguments; +} + +void Phaser::initialiseRNG(po::variables_map const& _arguments) +{ + uint32_t seed; + if (_arguments.count("seed") > 0) + seed = _arguments["seed"].as(); + else + seed = SimulationRNG::generateSeed(); + + SimulationRNG::reset(seed); + if (_arguments["show-seed"].as()) + cout << "Random seed: " << seed << endl; +} + +AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map const& _arguments) +{ + return { + _arguments.count("rounds") > 0 ? static_cast>(_arguments["rounds"].as()) : nullopt, + _arguments.count("population-autosave") > 0 ? static_cast>(_arguments["population-autosave"].as()) : nullopt, + !_arguments["no-randomise-duplicates"].as(), + _arguments["min-chromosome-length"].as(), + _arguments["max-chromosome-length"].as(), + _arguments["show-initial-population"].as(), + _arguments["show-only-top-chromosome"].as(), + !_arguments["hide-round"].as(), + _arguments["show-cache-stats"].as(), + }; +} + +void Phaser::runPhaser(po::variables_map const& _arguments) +{ + auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments); + auto cacheOptions = ProgramCacheFactory::Options::fromCommandLine(_arguments); + auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments); + auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments); + + vector programs = ProgramFactory::build(programOptions); + vector> programCaches = ProgramCacheFactory::build(cacheOptions, programs); + + unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, programs, programCaches); + Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); + + if (_arguments["mode"].as() == PhaserMode::RunAlgorithm) + runAlgorithm(_arguments, move(population), move(programCaches)); + else + printOptimisedProgramsOrASTs(_arguments, population, move(programs), _arguments["mode"].as()); +} + +void Phaser::runAlgorithm( + po::variables_map const& _arguments, + Population _population, + vector> _programCaches +) +{ + auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); + + unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( + algorithmOptions, + _population.individuals().size() + ); + + AlgorithmRunner algorithmRunner(move(_population), move(_programCaches), buildAlgorithmRunnerOptions(_arguments), cout); + algorithmRunner.run(*geneticAlgorithm); +} + +void Phaser::printOptimisedProgramsOrASTs( + po::variables_map const& _arguments, + Population const& _population, + vector _programs, + PhaserMode phaserMode +) +{ + assert(phaserMode == PhaserMode::PrintOptimisedPrograms || phaserMode == PhaserMode::PrintOptimisedASTs); + assert(_programs.size() == _arguments["input-files"].as>().size()); + + if (_population.individuals().size() == 0) + { + cout << "" << endl; + return; + } + + vector const& paths = _arguments["input-files"].as>(); + for (auto& individual: _population.individuals()) + { + cout << "Chromosome: " << individual.chromosome << endl; + + for (size_t i = 0; i < _programs.size(); ++i) + { + for (size_t j = 0; j < _arguments["chromosome-repetitions"].as(); ++j) + _programs[i].optimise(individual.chromosome.optimisationSteps()); + + cout << "Program: " << paths[i] << endl; + if (phaserMode == PhaserMode::PrintOptimisedPrograms) + cout << _programs[i] << endl; + else + cout << _programs[i].toJson() << endl; + } + } +} diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h new file mode 100644 index 000000000..77e9e48c8 --- /dev/null +++ b/tools/yulPhaser/Phaser.h @@ -0,0 +1,249 @@ +/* + 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 . +*/ +/** + * Contains the main class that controls yul-phaser based on command-line parameters and + * associated factories for building instances of phaser's components. + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +namespace solidity::langutil +{ + +class CharStream; + +} + +namespace solidity::phaser +{ + +class FitnessMetric; +class GeneticAlgorithm; +class Population; +class Program; +class ProgramCache; + +enum class PhaserMode +{ + RunAlgorithm, + PrintOptimisedPrograms, + PrintOptimisedASTs, +}; + +enum class Algorithm +{ + Random, + GEWEP, +}; + +enum class MetricChoice +{ + CodeSize, + RelativeCodeSize, +}; + +enum class MetricAggregatorChoice +{ + Average, + Sum, + Maximum, + Minimum, +}; + +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::PhaserMode& _phaserMode); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::PhaserMode _phaserMode); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricChoice _metric); +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricAggregatorChoice& _aggregator); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::MetricAggregatorChoice _aggregator); + +/** + * Builds and validates instances of @a GeneticAlgorithm and its derived classes. + */ +class GeneticAlgorithmFactory +{ +public: + struct Options + { + Algorithm algorithm; + size_t minChromosomeLength; + size_t maxChromosomeLength; + std::optional randomElitePoolSize; + double gewepMutationPoolSize; + double gewepCrossoverPoolSize; + double gewepRandomisationChance; + double gewepDeletionVsAdditionChance; + std::optional gewepGenesToRandomise; + std::optional gewepGenesToAddOrDelete; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::unique_ptr build( + Options const& _options, + size_t _populationSize + ); +}; + +/** + * Builds and validates instances of @a FitnessMetric and its derived classes. + */ +class FitnessMetricFactory +{ +public: + struct Options + { + MetricChoice metric; + MetricAggregatorChoice metricAggregator; + size_t relativeMetricScale; + size_t chromosomeRepetitions; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::unique_ptr build( + Options const& _options, + std::vector _programs, + std::vector> _programCaches + ); +}; + +/** + * Builds and validates instances of @a Population. + */ +class PopulationFactory +{ +public: + struct Options + { + size_t minChromosomeLength; + size_t maxChromosomeLength; + std::vector population; + std::vector randomPopulation; + std::vector populationFromFile; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static Population build( + Options const& _options, + std::shared_ptr _fitnessMetric + ); + static Population buildFromStrings( + std::vector const& _geneSequences, + std::shared_ptr _fitnessMetric + ); + static Population buildRandom( + size_t _populationSize, + size_t _minChromosomeLength, + size_t _maxChromosomeLength, + std::shared_ptr _fitnessMetric + ); + static Population buildFromFile( + std::string const& _filePath, + std::shared_ptr _fitnessMetric + ); +}; + +/** + * Builds and validates instances of @a ProgramCache. + */ +class ProgramCacheFactory +{ +public: + struct Options + { + bool programCacheEnabled; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::vector> build( + Options const& _options, + std::vector _programs + ); +}; + +/** + * Builds and validates instances of @a Program. + */ +class ProgramFactory +{ +public: + struct Options + { + std::vector inputFiles; + std::string prefix; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::vector build(Options const& _options); + +private: + static langutil::CharStream loadSource(std::string const& _sourcePath); +}; + +/** + * Main class that controls yul-phaser based on command-line parameters. The class is responsible + * for command-line parsing, initialisation of global objects (like the random number generator), + * creating instances of main components using factories and feeding them into @a AlgorithmRunner. + */ +class Phaser +{ +public: + static void main(int argc, char** argv); + +private: + struct CommandLineDescription + { + boost::program_options::options_description keywordDescription; + boost::program_options::positional_options_description positionalDescription; + }; + + static CommandLineDescription buildCommandLineDescription(); + static std::optional parseCommandLine(int _argc, char** _argv); + static void initialiseRNG(boost::program_options::variables_map const& _arguments); + static AlgorithmRunner::Options buildAlgorithmRunnerOptions(boost::program_options::variables_map const& _arguments); + + static void runPhaser(boost::program_options::variables_map const& _arguments); + static void runAlgorithm( + boost::program_options::variables_map const& _arguments, + Population _population, + std::vector> _programCaches + ); + static void printOptimisedProgramsOrASTs( + boost::program_options::variables_map const& _arguments, + Population const& _population, + std::vector _programs, + PhaserMode phaserMode + ); +}; + +} diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index b39f5dad5..b336daf73 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -42,8 +43,7 @@ ostream& operator<<(ostream& _stream, Population const& _population); ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) { - _stream << "Fitness: " << _individual.fitness; - _stream << ", optimisations: " << _individual.chromosome; + _stream << _individual.fitness << " " << _individual.chromosome; return _stream; } @@ -58,7 +58,7 @@ bool phaser::isFitter(Individual const& a, Individual const& b) } Population Population::makeRandom( - shared_ptr _fitnessMetric, + shared_ptr _fitnessMetric, size_t _size, function _chromosomeLengthGenerator ) @@ -71,7 +71,7 @@ Population Population::makeRandom( } Population Population::makeRandom( - shared_ptr _fitnessMetric, + shared_ptr _fitnessMetric, size_t _size, size_t _minChromosomeLength, size_t _maxChromosomeLength @@ -93,15 +93,45 @@ Population Population::select(Selection const& _selection) const return Population(m_fitnessMetric, selectedIndividuals); } +Population Population::mutate(Selection const& _selection, function _mutation) const +{ + vector mutatedIndividuals; + for (size_t i: _selection.materialise(m_individuals.size())) + mutatedIndividuals.emplace_back(_mutation(m_individuals[i].chromosome), *m_fitnessMetric); + + return Population(m_fitnessMetric, mutatedIndividuals); +} + +Population Population::crossover(PairSelection const& _selection, function _crossover) const +{ + vector crossedIndividuals; + for (auto const& [i, j]: _selection.materialise(m_individuals.size())) + { + auto childChromosome = _crossover( + m_individuals[i].chromosome, + m_individuals[j].chromosome + ); + crossedIndividuals.emplace_back(move(childChromosome), *m_fitnessMetric); + } + + return Population(m_fitnessMetric, crossedIndividuals); +} + +namespace solidity::phaser +{ + Population operator+(Population _a, Population _b) { // This operator is meant to be used only with populations sharing the same metric (and, to make // things simple, "the same" here means the same exact object in memory). assert(_a.m_fitnessMetric == _b.m_fitnessMetric); + using ::operator+; // Import the std::vector concat operator from CommonData.h return Population(_a.m_fitnessMetric, move(_a.m_individuals) + move(_b.m_individuals)); } +} + bool Population::operator==(Population const& _other) const { // We consider populations identical only if they share the same exact instance of the metric. @@ -120,7 +150,7 @@ ostream& phaser::operator<<(ostream& _stream, Population const& _population) } vector Population::chromosomesToIndividuals( - FitnessMetric const& _fitnessMetric, + FitnessMetric& _fitnessMetric, vector _chromosomes ) { diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index f4f42346d..40d51498b 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -28,17 +29,7 @@ namespace solidity::phaser { -class Population; - -} - -// This operator+ must be declared in the global namespace. Otherwise it would shadow global -// operator+ overloads from CommonData.h (e.g. the one for vector) in the namespace it was declared in. -solidity::phaser::Population operator+(solidity::phaser::Population _a, solidity::phaser::Population _b); - -namespace solidity::phaser -{ - +class PairSelection; class Selection; /** @@ -53,7 +44,7 @@ struct Individual Individual(Chromosome _chromosome, size_t _fitness): chromosome(std::move(_chromosome)), fitness(_fitness) {} - Individual(Chromosome _chromosome, FitnessMetric const& _fitnessMetric): + Individual(Chromosome _chromosome, FitnessMetric& _fitnessMetric): chromosome(std::move(_chromosome)), fitness(_fitnessMetric.evaluate(chromosome)) {} @@ -83,7 +74,7 @@ class Population { public: explicit Population( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, std::vector _chromosomes = {} ): Population( @@ -92,21 +83,24 @@ public: ) {} static Population makeRandom( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, size_t _size, std::function _chromosomeLengthGenerator ); static Population makeRandom( - std::shared_ptr _fitnessMetric, + std::shared_ptr _fitnessMetric, size_t _size, size_t _minChromosomeLength, size_t _maxChromosomeLength ); Population select(Selection const& _selection) const; - friend Population (::operator+)(Population _a, Population _b); + Population mutate(Selection const& _selection, std::function _mutation) const; + Population crossover(PairSelection const& _selection, std::function _crossover) const; - std::shared_ptr fitnessMetric() const { return m_fitnessMetric; } + friend Population operator+(Population _a, Population _b); + + std::shared_ptr fitnessMetric() { return m_fitnessMetric; } std::vector const& individuals() const { return m_individuals; } static size_t uniformChromosomeLength(size_t _min, size_t _max) { return SimulationRNG::uniformInt(_min, _max); } @@ -118,17 +112,17 @@ public: friend std::ostream& operator<<(std::ostream& _stream, Population const& _population); private: - explicit Population(std::shared_ptr _fitnessMetric, std::vector _individuals): + explicit Population(std::shared_ptr _fitnessMetric, std::vector _individuals): m_fitnessMetric(std::move(_fitnessMetric)), m_individuals{sortedIndividuals(std::move(_individuals))} {} static std::vector chromosomesToIndividuals( - FitnessMetric const& _fitnessMetric, + FitnessMetric& _fitnessMetric, std::vector _chromosomes ); static std::vector sortedIndividuals(std::vector _individuals); - std::shared_ptr m_fitnessMetric; + std::shared_ptr m_fitnessMetric; std::vector m_individuals; }; diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 42cc8bf61..a457750d2 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -17,17 +17,16 @@ #include -#include - #include #include -#include +#include #include #include #include #include #include +#include #include #include #include @@ -57,6 +56,16 @@ ostream& operator<<(ostream& _stream, Program const& _program); } +ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors) +{ + SourceReferenceFormatter formatter(_outputStream); + + for (auto const& error: _errors) + formatter.printErrorInformation(*error); + + return _outputStream; +} + Program::Program(Program const& program): m_ast(make_unique(get(ASTCopier{}(*program.m_ast)))), m_dialect{program.m_dialect}, @@ -64,16 +73,29 @@ Program::Program(Program const& program): { } -Program Program::load(CharStream& _sourceCode) +variant Program::load(CharStream& _sourceCode) { // ASSUMPTION: parseSource() rewinds the stream on its own Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - unique_ptr ast = parseSource(dialect, _sourceCode); - unique_ptr analysisInfo = analyzeAST(dialect, *ast); + + variant, ErrorList> astOrErrors = parseObject(dialect, _sourceCode); + if (holds_alternative(astOrErrors)) + return get(astOrErrors); + + variant, ErrorList> analysisInfoOrErrors = analyzeAST( + dialect, + *get>(astOrErrors) + ); + if (holds_alternative(analysisInfoOrErrors)) + return get(analysisInfoOrErrors); Program program( dialect, - disambiguateAST(dialect, *ast, *analysisInfo) + disambiguateAST( + dialect, + *get>(astOrErrors), + *get>(analysisInfoOrErrors) + ) ); program.optimise({ FunctionHoister::name, @@ -100,21 +122,47 @@ string Program::toJson() const return jsonPrettyPrint(serializedAst); } -unique_ptr Program::parseSource(Dialect const& _dialect, CharStream _source) +variant, ErrorList> Program::parseObject(Dialect const& _dialect, CharStream _source) { ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(move(_source)); - Parser parser(errorReporter, _dialect); - unique_ptr ast = parser.parse(scanner, false); - assertThrow(ast != nullptr, InvalidProgram, "Error parsing source"); - assert(errorReporter.errors().empty()); + ObjectParser parser(errorReporter, _dialect); + shared_ptr object = parser.parse(scanner, false); + if (object == nullptr || !errorReporter.errors().empty()) + // NOTE: It's possible to get errors even if the returned object is non-null. + // For example when there are errors in a nested object. + return errors; - return ast; + Object* deployedObject = nullptr; + if (object->subObjects.size() > 0) + for (auto& subObject: object->subObjects) + // solc --ir produces an object with a subobject of the same name as the outer object + // but suffixed with "_deployed". + // The other object references the nested one which makes analysis fail. Below we try to + // extract just the nested one for that reason. This is just a heuristic. If there's no + // subobject with such a suffix we fall back to accepting the whole object as is. + if (subObject != nullptr && subObject->name.str() == object->name.str() + "_deployed") + { + deployedObject = dynamic_cast(subObject.get()); + if (deployedObject != nullptr) + break; + } + Object* selectedObject = (deployedObject != nullptr ? deployedObject : object.get()); + + // NOTE: I'm making a copy of the whole AST to get unique_ptr rather than shared_ptr. + // This is a slight performance hit but it's much less than the parsing itself. + // unique_ptr lets me be sure that two Program instances can never share the AST by mistake. + // The public API of the class does not provide access to the smart pointer so it won't be hard + // to switch to shared_ptr if the copying turns out to be an issue (though it would be better + // to refactor ObjectParser and Object to use unique_ptr instead). + auto astCopy = make_unique(get(ASTCopier{}(*selectedObject->code))); + + return variant, ErrorList>(move(astCopy)); } -unique_ptr Program::analyzeAST(Dialect const& _dialect, Block const& _ast) +variant, ErrorList> Program::analyzeAST(Dialect const& _dialect, Block const& _ast) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -122,10 +170,11 @@ unique_ptr Program::analyzeAST(Dialect const& _dialect, Block c AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect); bool analysisSuccessful = analyzer.analyze(_ast); - assertThrow(analysisSuccessful, InvalidProgram, "Error analyzing source"); - assert(errorReporter.errors().empty()); + if (!analysisSuccessful) + return errors; - return analysisInfo; + assert(errorReporter.errors().empty()); + return variant, ErrorList>(move(analysisInfo)); } unique_ptr Program::disambiguateAST( diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 5e240e98d..10fed6de2 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -20,10 +20,13 @@ #include #include +#include + #include #include #include #include +#include #include namespace solidity::langutil @@ -41,6 +44,13 @@ struct Dialect; } +namespace std +{ + +std::ostream& operator<<(std::ostream& _outputStream, solidity::langutil::ErrorList const& _errors); + +} + namespace solidity::phaser { @@ -65,7 +75,7 @@ public: Program operator=(Program const& program) = delete; Program operator=(Program&& program) = delete; - static Program load(langutil::CharStream& _sourceCode); + static std::variant load(langutil::CharStream& _sourceCode); void optimise(std::vector const& _optimisationSteps); size_t codeSize() const { return computeCodeSize(*m_ast); } @@ -84,11 +94,11 @@ private: m_nameDispenser(_dialect, *m_ast, {}) {} - static std::unique_ptr parseSource( + static std::variant, langutil::ErrorList> parseObject( yul::Dialect const& _dialect, langutil::CharStream _source ); - static std::unique_ptr analyzeAST( + static std::variant, langutil::ErrorList> analyzeAST( yul::Dialect const& _dialect, yul::Block const& _ast ); diff --git a/tools/yulPhaser/ProgramCache.cpp b/tools/yulPhaser/ProgramCache.cpp new file mode 100644 index 000000000..7acec16bd --- /dev/null +++ b/tools/yulPhaser/ProgramCache.cpp @@ -0,0 +1,151 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +using namespace std; +using namespace solidity::yul; +using namespace solidity::phaser; + +CacheStats& CacheStats::operator+=(CacheStats const& _other) +{ + hits += _other.hits; + misses += _other.misses; + totalCodeSize += _other.totalCodeSize; + + for (auto& [round, count]: _other.roundEntryCounts) + if (roundEntryCounts.find(round) != roundEntryCounts.end()) + roundEntryCounts.at(round) += count; + else + roundEntryCounts.insert({round, count}); + + return *this; +} + +bool CacheStats::operator==(CacheStats const& _other) const +{ + return + hits == _other.hits && + misses == _other.misses && + totalCodeSize == _other.totalCodeSize && + roundEntryCounts == _other.roundEntryCounts; +} + +Program ProgramCache::optimiseProgram( + string const& _abbreviatedOptimisationSteps, + size_t _repetitionCount +) +{ + string targetOptimisations = _abbreviatedOptimisationSteps; + for (size_t i = 1; i < _repetitionCount; ++i) + targetOptimisations += _abbreviatedOptimisationSteps; + + size_t prefixSize = 0; + for (size_t i = 1; i <= targetOptimisations.size(); ++i) + { + auto const& pair = m_entries.find(targetOptimisations.substr(0, i)); + if (pair != m_entries.end()) + { + pair->second.roundNumber = m_currentRound; + ++prefixSize; + ++m_hits; + } + else + break; + } + + Program intermediateProgram = ( + prefixSize == 0 ? + m_program : + m_entries.at(targetOptimisations.substr(0, prefixSize)).program + ); + + for (size_t i = prefixSize + 1; i <= targetOptimisations.size(); ++i) + { + string stepName = OptimiserSuite::stepAbbreviationToNameMap().at(targetOptimisations[i - 1]); + intermediateProgram.optimise({stepName}); + + m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}}); + ++m_misses; + } + + return intermediateProgram; +} + +void ProgramCache::startRound(size_t _roundNumber) +{ + assert(_roundNumber > m_currentRound); + m_currentRound = _roundNumber; + + for (auto pair = m_entries.begin(); pair != m_entries.end();) + { + assert(pair->second.roundNumber < m_currentRound); + + if (pair->second.roundNumber < m_currentRound - 1) + m_entries.erase(pair++); + else + ++pair; + } +} + +void ProgramCache::clear() +{ + m_entries.clear(); + m_currentRound = 0; +} + +Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) const +{ + auto const& pair = m_entries.find(_abbreviatedOptimisationSteps); + if (pair == m_entries.end()) + return nullptr; + + return &(pair->second.program); +} + +CacheStats ProgramCache::gatherStats() const +{ + return { + /* hits = */ m_hits, + /* misses = */ m_misses, + /* totalCodeSize = */ calculateTotalCachedCodeSize(), + /* roundEntryCounts = */ countRoundEntries(), + }; +} + +size_t ProgramCache::calculateTotalCachedCodeSize() const +{ + size_t size = 0; + for (auto const& pair: m_entries) + size += pair.second.program.codeSize(); + + return size; +} + +map ProgramCache::countRoundEntries() const +{ + map counts; + for (auto& pair: m_entries) + if (counts.find(pair.second.roundNumber) != counts.end()) + ++counts.at(pair.second.roundNumber); + else + counts.insert({pair.second.roundNumber, 1}); + + return counts; +} diff --git a/tools/yulPhaser/ProgramCache.h b/tools/yulPhaser/ProgramCache.h new file mode 100644 index 000000000..8cc830098 --- /dev/null +++ b/tools/yulPhaser/ProgramCache.h @@ -0,0 +1,117 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include +#include + +namespace solidity::phaser +{ + +/** + * Structure used by @a ProgramCache to store intermediate programs and metadata associated + * with them. + */ +struct CacheEntry +{ + Program program; + size_t roundNumber; + + CacheEntry(Program _program, size_t _roundNumber): + program(std::move(_program)), + roundNumber(_roundNumber) {} +}; + +/** + * Stores statistics about current cache usage. + */ +struct CacheStats +{ + size_t hits; + size_t misses; + size_t totalCodeSize; + std::map roundEntryCounts; + + CacheStats& operator+=(CacheStats const& _other); + CacheStats operator+(CacheStats const& _other) const { return CacheStats(*this) += _other; } + + bool operator==(CacheStats const& _other) const; + bool operator!=(CacheStats const& _other) const { return !(*this == _other); } +}; + +/** + * Class that optimises programs one step at a time which allows it to store and later reuse the + * results of the intermediate steps. + * + * The cache keeps track of the current round number and associates newly created entries with it. + * @a startRound() must be called at the beginning of a round so that entries that are too old + * can be purged. The current strategy is to store programs corresponding to all possible prefixes + * encountered in the current and the previous rounds. Entries older than that get removed to + * conserve memory. + * + * @a gatherStats() allows getting statistics useful for determining cache effectiveness. + * + * The current strategy does speed things up (about 4:1 hit:miss ratio observed in my limited + * experiments) but there's room for improvement. We could fit more useful programs in + * the cache by being more picky about which ones we choose. + * + * There is currently no way to purge entries without starting a new round. Since the programs + * take a lot of memory, this may lead to the cache eating up all the available RAM if sequences are + * long and programs large. A limiter based on entry count or total program size would be useful. + */ +class ProgramCache +{ +public: + explicit ProgramCache(Program _program): + m_program(std::move(_program)) {} + + Program optimiseProgram( + std::string const& _abbreviatedOptimisationSteps, + size_t _repetitionCount = 1 + ); + void startRound(size_t _nextRoundNumber); + void clear(); + + size_t size() const { return m_entries.size(); } + Program const* find(std::string const& _abbreviatedOptimisationSteps) const; + bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; } + + CacheStats gatherStats() const; + + std::map const& entries() const { return m_entries; }; + Program const& program() const { return m_program; } + size_t currentRound() const { return m_currentRound; } + +private: + size_t calculateTotalCachedCodeSize() const; + std::map countRoundEntries() const; + + // The best matching data structure here would be a trie of chromosome prefixes but since + // the programs are orders of magnitude larger than the prefixes, it does not really matter. + // A map should be good enough. + std::map m_entries; + + Program m_program; + size_t m_currentRound = 0; + size_t m_hits = 0; + size_t m_misses = 0; +}; + +} diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 1103db5a8..2b596f4ed 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -52,5 +52,5 @@ uint32_t SimulationRNG::generateSeed() // This is not a secure way to seed the generator but it's good enough for simulation purposes. // The only thing that matters for us is that the sequence is different on each run and that // it fits the expected distribution. It does not have to be 100% unpredictable. - return time(0); + return time(nullptr); } diff --git a/tools/yulPhaser/main.cpp b/tools/yulPhaser/main.cpp index 321ebe088..d0c00fcda 100644 --- a/tools/yulPhaser/main.cpp +++ b/tools/yulPhaser/main.cpp @@ -16,157 +16,74 @@ */ #include -#include -#include -#include -#include -#include +#include -#include -#include -#include - -#include -#include +#include #include -#include - -using namespace std; -using namespace solidity::langutil; -using namespace solidity::phaser; -using namespace solidity::util; - -namespace po = boost::program_options; - -namespace -{ - -struct CommandLineParsingResult -{ - int exitCode; - po::variables_map arguments; -}; - - -void initializeRNG(po::variables_map const& arguments) -{ - uint32_t seed; - if (arguments.count("seed") > 0) - seed = arguments["seed"].as(); - else - seed = SimulationRNG::generateSeed(); - - SimulationRNG::reset(seed); - cout << "Random seed: " << seed << endl; -} - -CharStream loadSource(string const& _sourcePath) -{ - assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist"); - - string sourceCode = readFileAsString(_sourcePath); - return CharStream(sourceCode, _sourcePath); -} - -void runAlgorithm(string const& _sourcePath) -{ - constexpr size_t populationSize = 20; - constexpr size_t minChromosomeLength = 12; - constexpr size_t maxChromosomeLength = 30; - - CharStream sourceCode = loadSource(_sourcePath); - shared_ptr fitnessMetric = make_shared(Program::load(sourceCode), 5); - auto population = Population::makeRandom( - fitnessMetric, - populationSize, - minChromosomeLength, - maxChromosomeLength - ); - RandomAlgorithm( - population, - cout, - { - /* elitePoolSize = */ 1.0 / populationSize, - /* minChromosomeLength = */ minChromosomeLength, - /* maxChromosomeLength = */ maxChromosomeLength, - } - ).run(); -} - -CommandLineParsingResult parseCommandLine(int argc, char** argv) -{ - po::options_description description( - "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" - "\n" - "Usage: yul-phaser [options] \n" - "Reads as Yul code and tries to find the best order in which to run optimisation" - " phases using a genetic algorithm.\n" - "Example:\n" - "yul-phaser program.yul\n" - "\n" - "Allowed options", - po::options_description::m_default_line_length, - po::options_description::m_default_line_length - 23 - ); - - description.add_options() - ("help", "Show help message and exit.") - ("input-file", po::value()->required(), "Input file") - ("seed", po::value(), "Seed for the random number generator") - ; - - po::positional_options_description positionalDescription; - po::variables_map arguments; - positionalDescription.add("input-file", 1); - po::notify(arguments); - - try - { - po::command_line_parser parser(argc, argv); - parser.options(description).positional(positionalDescription); - po::store(parser.run(), arguments); - } - catch (po::error const & _exception) - { - cerr << _exception.what() << endl; - return {1, move(arguments)}; - } - - if (arguments.count("help") > 0) - { - cout << description << endl; - return {2, move(arguments)}; - } - - if (arguments.count("input-file") == 0) - { - cerr << "Missing argument: input-file." << endl; - return {1, move(arguments)}; - } - - return {0, arguments}; -} - -} int main(int argc, char** argv) { - CommandLineParsingResult parsingResult = parseCommandLine(argc, argv); - if (parsingResult.exitCode != 0) - return parsingResult.exitCode; - - initializeRNG(parsingResult.arguments); - try { - runAlgorithm(parsingResult.arguments["input-file"].as()); + solidity::phaser::Phaser::main(argc, argv); + return 0; } - catch (InvalidProgram const& _exception) + catch (boost::program_options::error const& exception) { - cerr << "ERROR: " << _exception.what() << endl; + // Bad input data. Invalid command-line parameters. + + std::cerr << std::endl; + std::cerr << "ERROR: " << exception.what() << std::endl; return 1; } + catch (solidity::phaser::BadInput const& exception) + { + // Bad input data. Syntax errors in the input program, semantic errors in command-line + // parameters, etc. - return 0; + std::cerr << std::endl; + std::cerr << "ERROR: " << exception.what() << std::endl; + return 1; + } + catch (solidity::util::Exception const& exception) + { + // Something's seriously wrong. Probably a bug in the program or a missing handler (which + // is really also a bug). The exception should have been handled gracefully by this point + // if it's something that can happen in normal usage. E.g. an error in the input or a + // failure of some part of the system that's outside of control of the application (disk, + // network, etc.). The bug should be reported and investigated so our job here is just to + // provide as much useful information about it as possible. + + std::cerr << std::endl; + std::cerr << "UNCAUGHT EXCEPTION!" << std::endl; + + // We can print some useful diagnostic info for this particular exception type. + std::cerr << "Location: " << exception.lineInfo() << std::endl; + + char const* const* function = boost::get_error_info(exception); + if (function != nullptr) + std::cerr << "Function: " << *function << std::endl; + + // Let it crash. The terminate() will print some more stuff useful for debugging like + // what() and the actual exception type. + throw; + } + catch (std::exception const&) + { + // Again, probably a bug but this time it's just plain std::exception so there's no point + // in doing anything special. terminate() will do an adequate job. + std::cerr << std::endl; + std::cerr << "UNCAUGHT EXCEPTION!" << std::endl; + throw; + } + catch (...) + { + // Some people don't believe these exist. + // I have no idea what this is and it's flying towards me so technically speaking it's an + // unidentified flying object. + std::cerr << std::endl; + std::cerr << "UFO SPOTTED!" << std::endl; + throw; + } }