diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b0197625..91ba3a4e0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,8 +21,8 @@ parameters: default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a4d5271b5552139d9f2caefc50d42f401bf74132cf8f253e199e11c80ab42de" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:efaabb3c143f64171be596932c62013bcfd7f73b1fbcb832025a34dd2b6e6922" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-3 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:6fa6914bd81abcac4b162c738e6ff05d87cefe7655e3859c7a827e5a8ec20dc7" emscripten-docker-image: type: string default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc" @@ -487,7 +487,7 @@ jobs: <<: *build_ubuntu2004 environment: CMAKE_BUILD_TYPE: Debug - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF + CMAKE_OPTIONS: -DCMAKE_CXX_STANDARD=20 -DUSE_CVC4=OFF MAKEFLAGS: -j 10 steps: - checkout @@ -500,7 +500,6 @@ jobs: CC: clang CXX: clang++ TERM: xterm - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake MAKEFLAGS: -j 3 steps: - checkout @@ -893,13 +892,6 @@ workflows: - t_ubu_release_cli: *workflow_ubuntu2004_release - t_ubu_release_soltest: *workflow_ubuntu2004_release - # ASan build and tests - - b_ubu_asan: *workflow_trigger_on_tags - - b_ubu_asan_clang: *workflow_trigger_on_tags - - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan - - t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang - - t_ubu_asan_cli: *workflow_ubuntu2004_asan - # Emscripten build and selected tests - b_ems: *workflow_trigger_on_tags - t_ems_solcjs: *workflow_emscripten @@ -926,3 +918,10 @@ workflows: # Code Coverage enabled build and tests - b_ubu_codecov: *workflow_trigger_on_tags - t_ubu_codecov: *workflow_ubuntu2004_codecov + + # ASan build and tests + - b_ubu_asan: *workflow_trigger_on_tags + - b_ubu_asan_clang: *workflow_trigger_on_tags + - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan + - t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang + - t_ubu_asan_cli: *workflow_ubuntu2004_asan diff --git a/Changelog.md b/Changelog.md index 930f04152..01a098d8f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,17 +5,37 @@ ### 0.7.1 (unreleased) Language Features: + * Allow function definitions outside of contracts, behaving much like internal library functions. Compiler Features: + * SMTChecker: Add underflow and overflow as verification conditions in the CHC engine. * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. + * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. + * SMTChecker: Support conditional operator. + * SMTChecker: Support bitwise or, xor and not operators. Bugfixes: + * AST: Remove ``null`` member values also when the compiler is used in standard-json-mode. + * Immutables: Properly treat complex assignment and increment/decrement as both reading and writing and thus disallow it everywhere for immutable variables. * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. + * Scanner: Fix bug where whitespace would be allowed within the ``->`` token (e.g. ``function f() - > x {}`` becomes invalid in inline assembly and Yul). * SMTChecker: Fix internal error in BMC function inlining. + * SMTChecker: Fix internal error on array implicit conversion. * SMTChecker: Fix internal error on fixed bytes index access. + * SMTChecker: Fix internal error on lvalue unary operators with tuples. + * SMTChecker: Fix internal error on tuple assignment. + * SMTChecker: Fix internal error on tuples of one element that have tuple type. + * SMTChecker: Fix soundness of array ``pop``. * References Resolver: Fix internal bug when using constructor for library. + * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. + * Yul Optimizer: Ensure that Yul keywords are not mistakenly used by the NameDispenser and VarNameCleaners. The bug would manifest as uncompilable code. + * Type Checker: Disallow signed literals as exponent in exponentiation operator. + * Allow `type(Contract).name` for abstract contracts and interfaces. + * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. + * Type Checker: Disallow ``using for`` directive inside interfaces. + * Immutables: Disallow assigning immutables more than once during their declaration. ### 0.7.0 (2020-07-28) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 8cdc00591..069098f20 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -6,6 +6,9 @@ - [ ] Readthedocs account, access to the Solidity project - [ ] Write access to https://github.com/ethereum/homebrew-ethereum +### Documentation check + - [ ] Run `make linkcheck` from within `docs/` and fix any broken links it finds. Ignore false positives caused by `href` anchors and dummy links not meant to work. + ### Blog Post - [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts. diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 3bcdad47b..653c5fdc3 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -157,9 +157,10 @@ elseif (DEFINED MSVC) add_compile_options(/wd4800) # disable forcing value to bool 'true' or 'false' (performance warning) (4800) add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions - add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) + add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) add_compile_options(-DBOOST_REGEX_NO_LIB) # disable automatic boost::regex library selection add_compile_options(-D_REGEX_MAX_STACK_COUNT=200000L) # increase std::regex recursion depth limit + add_compile_options(/permissive-) # specify standards conformance mode to the compiler # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") diff --git a/cmake/EthToolchains.cmake b/cmake/EthToolchains.cmake index a4263b7df..adf0a20a5 100644 --- a/cmake/EthToolchains.cmake +++ b/cmake/EthToolchains.cmake @@ -1,3 +1,8 @@ +# Require C++17. +set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + if(NOT CMAKE_TOOLCHAIN_FILE) # Use default toolchain file if none is provided. set( diff --git a/cmake/toolchains/cxx20.cmake b/cmake/toolchains/cxx20.cmake deleted file mode 100644 index ad34e5749..000000000 --- a/cmake/toolchains/cxx20.cmake +++ /dev/null @@ -1,4 +0,0 @@ -# Require C++20. -set(CMAKE_CXX_STANDARD 20) # This requires at least CMake 3.12 to understand this C++20 flag -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/toolchains/default.cmake b/cmake/toolchains/default.cmake index 07fc80e8e..e69de29bb 100644 --- a/cmake/toolchains/default.cmake +++ b/cmake/toolchains/default.cmake @@ -1,4 +0,0 @@ -# Require C++17. -set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 deleted file mode 100644 index 91f59d78c..000000000 --- a/docs/Solidity.g4 +++ /dev/null @@ -1,487 +0,0 @@ -// 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 ( ~';' )* ';' ; - -pragmaName - : identifier ; - -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' '(' mappingKey '=>' typeName ')' ; - -mappingKey - : elementaryTypeName - | userDefinedTypeName ; - -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 - | unicodeStringLiteral - | 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 | assemblyIdentifier ; - -assemblyCall - : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; - -assemblyLocalDefinition - : 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ; - -assemblyAssignment - : assemblyIdentifierList ':=' assemblyExpression ; - -assemblyIdentifierList - : assemblyIdentifier ( ',' assemblyIdentifier )* ; - -assemblyIdentifier - : 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 ; - -// 'finney' and 'szabo' are no longer supported as denominations by latest Solidity. -numberLiteral - : (DecimalNumber | HexNumber) (NumberUnit | Gwei | Finney | Szabo)?; - -identifier - : (Gwei | Finney | Szabo | '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' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; - -Gwei: 'gwei' ; -Szabo: 'szabo' ; -Finney: 'finney' ; - -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* '\'' ; - -unicodeStringLiteral - : UnicodeStringLiteralFragment+ ; - -UnicodeStringLiteralFragment - : 'unicode"' DoubleQuotedStringCharacter* '"' - | 'unicode\'' SingleQuotedStringCharacter* '\'' ; - -fragment -DoubleQuotedStringCharacter - : ~["\r\n\\] | ('\\' .) ; - -fragment -SingleQuotedStringCharacter - : ~['\r\n\\] | ('\\' .) ; - -WS - : [ \t\r\n\u000C]+ -> skip ; - -COMMENT - : '/*' .*? '*/' -> channel(HIDDEN) ; - -LINE_COMMENT - : '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/brand-guide.rst b/docs/brand-guide.rst index 0e6400bed..dda438533 100644 --- a/docs/brand-guide.rst +++ b/docs/brand-guide.rst @@ -43,7 +43,7 @@ Solidity Logo License :alt: Creative Commons License The Solidity logo is distributed and licensed under a `Creative Commons -Attribution 4.0 International License `_. +Attribution 4.0 International License `_. This is the most permissive Creative Commons license and allows reuse and modifications for any purpose. diff --git a/docs/conf.py b/docs/conf.py index 1ff794722..191955c9a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,7 +39,9 @@ def setup(sphinx): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = [ 'sphinx_a4doc' ] + +a4_base_path = os.path.dirname(__file__) + '/grammar' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -83,7 +85,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts', 'types', 'examples'] +exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 33204f58d..06bc4189e 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -21,7 +21,7 @@ is copied to all the places in the code where they are accessed. For these value can sometimes be cheaper than immutable values. Not all types for constants and immutables are implemented at this time. The only supported types are -`strings `_ (only for constants) and `value types `_. +:ref:`strings ` (only for constants) and :ref:`value types `. :: diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 3183d3c3f..b7289e784 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -160,6 +160,6 @@ where the long hexadecimal number is equal to Additional Resources for Understanding Events ============================================== -- `Javascript documentation `_ -- `Example usage of events `_ -- `How to access them in js `_ +- `Javascript documentation `_ +- `Example usage of events `_ +- `How to access them in js `_ diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index 823e6e6ac..881b8f787 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -18,7 +18,7 @@ if they are marked ``virtual``. For details, please see :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >0.7.0 <0.8.0; contract owned { constructor() { owner = msg.sender; } diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 41174c958..e7c97c33d 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -6,6 +6,34 @@ Functions ********* +Functions can be defined inside and outside of contracts. + +Functions outside of a contract, also called "free functions", always have implicit ``internal`` +:ref:`visibility`. Their code is included in all contracts +that call them, similar to internal library functions. + +:: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0 <0.8.0; + + function sum(uint[] memory _arr) pure returns (uint s) { + for (uint i = 0; i < _arr.length; i++) + s += _arr[i]; + } + + contract ArrayExample { + bool found; + function f(uint[] memory _arr) public { + // This calls the free function internally. + // The compiler will add its code to the contract. + uint s = sum(_arr); + require(s >= 10); + found = true; + } + } + + .. _function-parameters-return-variables: Function Parameters and Return Variables diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index f628d49fb..533f74da4 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -331,7 +331,7 @@ Modifier Overriding =================== Function modifiers can override each other. This works in the same way as -`function overriding `_ (except that there is no overloading for modifiers). The +:ref:`function overriding ` (except that there is no overloading for modifiers). The ``virtual`` keyword must be used on the overridden modifier and the ``override`` keyword must be used in the overriding modifier: diff --git a/docs/contributing.rst b/docs/contributing.rst index ab5163e11..a5932c8fe 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -102,7 +102,7 @@ The ``./scripts/tests.sh`` script executes most Solidity tests automatically, including those bundled into the `Boost C++ Test Framework `_ application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command line tests and compilation tests. -The test system automatically tries try to discover the location of the ``evmone`` library +The test system automatically tries to discover the location of the ``evmone`` library starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``, @@ -254,7 +254,7 @@ inside the input. We have a specialized binary called ``solfuzzer`` which takes and fails whenever it encounters an internal compiler error, segmentation fault or similar, but does not fail if e.g., the code contains an error. This way, fuzzing tools can find internal problems in the compiler. -We mainly use `AFL `_ for fuzzing. You need to download and +We mainly use `AFL `_ for fuzzing. You need to download and install the AFL packages from your repositories (afl, afl-clang) or build them manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: @@ -388,7 +388,7 @@ local slang and references, making your language as clear to all readers as poss Title Case for Headings ----------------------- -Use `title case `_ for headings. This means capitalise all principal words in +Use `title case `_ for headings. This means capitalise all principal words in titles, but not articles, conjunctions, and prepositions unless they start the title. diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index b68de3cad..2ff293f48 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -114,7 +114,7 @@ In general, ECDSA signatures consist of two parameters, parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in -function `ecrecover `_ that +function :ref:`ecrecover ` that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message. @@ -127,7 +127,7 @@ apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into its constituent parts is a mess, so we use -`inline assembly `_ to do the job in the ``splitSignature`` +:doc:`inline assembly ` to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section). Computing the Message Hash diff --git a/docs/grammar.rst b/docs/grammar.rst index c7a22dd7e..d69f86b08 100644 --- a/docs/grammar.rst +++ b/docs/grammar.rst @@ -2,5 +2,12 @@ Language Grammar **************** -.. literalinclude:: Solidity.g4 - :language: antlr +.. a4:autogrammar:: Solidity + :only-reachable-from: Solidity.sourceUnit + :undocumented: + :cc-to-dash: + +.. a4:autogrammar:: SolidityLexer + :only-reachable-from: Solidity.sourceUnit + :fragments: + :cc-to-dash: \ No newline at end of file diff --git a/docs/grammar/Solidity.g4 b/docs/grammar/Solidity.g4 new file mode 100644 index 000000000..abf536cde --- /dev/null +++ b/docs/grammar/Solidity.g4 @@ -0,0 +1,511 @@ +/** + * Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. + */ +grammar Solidity; + +options { tokenVocab=SolidityLexer; } + +/** + * On top level, Solidity allows pragmas, import directives, and + * definitions of contracts, interfaces, libraries, structs and enums. + */ +sourceUnit: ( + pragmaDirective + | importDirective + | contractDefinition + | interfaceDefinition + | libraryDefinition + | functionDefinition + | structDefinition + | enumDefinition +)* EOF; + +//@doc: inline +pragmaDirective: Pragma PragmaToken+ PragmaSemicolon; + +/** + * Import directives import identifiers from different files. + */ +importDirective: + Import ( + (path (As unitAlias=identifier)?) + | (symbolAliases From path) + | (Mul As unitAlias=identifier From path) + ) Semicolon; +//@doc: inline +//@doc:name aliases +importAliases: symbol=identifier (As alias=identifier)?; +/** + * Path of a file to be imported. + */ +path: NonEmptyStringLiteral; +/** + * List of aliases for symbols to be imported. + */ +symbolAliases: LBrace aliases+=importAliases (Comma aliases+=importAliases)* RBrace; + +/** + * Top-level definition of a contract. + */ +contractDefinition: + Abstract? Contract name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of an interface. + */ +interfaceDefinition: + Interface name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of a library. + */ +libraryDefinition: Library name=identifier LBrace contractBodyElement* RBrace; + +//@doc:inline +inheritanceSpecifierList: + Is inheritanceSpecifiers+=inheritanceSpecifier + (Comma inheritanceSpecifiers+=inheritanceSpecifier)*?; +/** + * Inheritance specifier for contracts and interfaces. + * Can optionally supply base constructor arguments. + */ +inheritanceSpecifier: name=userDefinedTypeName arguments=callArgumentList?; + +/** + * Declarations that can be used in contracts, interfaces and libraries. + * + * Note that interfaces and libraries may not contain constructors, interfaces may not contain state variables + * and libraries may not contain fallback, receive functions nor non-constant state variables. + */ +contractBodyElement: + constructorDefinition + | functionDefinition + | modifierDefinition + | fallbackReceiveFunctionDefinition + | structDefinition + | enumDefinition + | stateVariableDeclaration + | eventDefinition + | usingDirective; +//@doc:inline +namedArgument: name=identifier Colon value=expression; +/** + * Arguments when calling a function or a similar callable object. + * The arguments are either given as comma separated list or as map of named arguments. + */ +callArgumentList: LParen ((expression (Comma expression)*)? | LBrace (namedArgument (Comma namedArgument)*)? RBrace) RParen; +/** + * Qualified name of a user defined type. + */ +userDefinedTypeName: identifier (Period identifier)*; + +/** + * Call to a modifier. If the modifier takes no arguments, the argument list can be skipped entirely + * (including opening and closing parentheses). + */ +modifierInvocation: identifier callArgumentList?; +/** + * Visibility for functions and function types. + */ +visibility: Internal | External | Private | Public; +/** + * A list of parameters, such as function arguments or return values. + */ +parameterList: parameters+=parameterDeclaration (Comma parameters+=parameterDeclaration)*; +//@doc:inline +parameterDeclaration: type=typeName location=dataLocation? name=identifier?; +/** + * Definition of a constructor. + * Must always supply an implementation. + * Note that specifying internal or public visibility is deprecated. + */ +constructorDefinition +locals[boolean payableSet = false, boolean visibilitySet = false] +: + Constructor LParen (arguments=parameterList)? RParen + ( + modifierInvocation + | {!$payableSet}? Payable {$payableSet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$visibilitySet}? Public {$visibilitySet = true;} + )* + body=block; + +/** + * State mutability for function types. + * The default mutability 'non-payable' is assumed if no mutability is specified. + */ +stateMutability: Pure | View | Payable; +/** + * An override specifier used for functions, modifiers or state variables. + * In cases where there are ambiguous declarations in several base contracts being overridden, + * a complete list of base contracts has to be given. + */ +overrideSpecifier: Override (LParen overrides+=userDefinedTypeName (Comma overrides+=userDefinedTypeName)* RParen)?; +/** + * The definition of contract, library and interface functions. + * Depending on the context in which the function is defined, further restrictions may apply, + * e.g. functions in interfaces have to be unimplemented, i.e. may not contain a body block. + */ +functionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Function (identifier | Fallback | Receive) + LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)? + (Semicolon | body=block); +/** + * The definition of a modifier. + * Note that within the body block of a modifier, the underscore cannot be used as identifier, + * but is used as placeholder statement for the body of a function to which the modifier is applied. + */ +modifierDefinition +locals[ + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Modifier name=identifier + (LParen (arguments=parameterList)? RParen)? + ( + {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definitions of the special fallback and receive functions. + */ +fallbackReceiveFunctionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + kind=(Fallback | Receive) LParen RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definition of a struct. Can occur at top-level within a source unit or within a contract, library or interface. + */ +structDefinition: Struct name=identifier LBrace members=structMember+ RBrace; +/** + * The declaration of a named struct member. + */ +structMember: type=typeName name=identifier Semicolon; +/** + * Definition of an enum. Can occur at top-level within a source unit or within a contract, library or interface. + */ +enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace; + +/** + * The declaration of a state variable. + */ +stateVariableDeclaration +locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false] +: + type=typeName + ( + {!$visibilitySet}? Public {$visibilitySet = true;} + | {!$visibilitySet}? Private {$visibilitySet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$constantnessSet}? Constant {$constantnessSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + | {!$constantnessSet}? Immutable {$constantnessSet = true;} + )* + name=identifier + (Assign initialValue=expression)? + Semicolon; + +/** + * Parameter of an event. + */ +eventParameter: type=typeName Indexed? name=identifier?; +/** + * Definition of an event. Can occur in contracts, libraries or interfaces. + */ +eventDefinition: + Event name=identifier + LParen (parameters+=eventParameter (Comma parameters+=eventParameter)*)? RParen + Anonymous? + Semicolon; + +/** + * Using directive to bind library functions to types. + * Can occur within contracts and libraries. + */ +usingDirective: Using userDefinedTypeName For (Mul | typeName) Semicolon; +/** + * A type name can be an elementary type, a function type, a mapping type, a user-defined type + * (e.g. a contract or struct) or an array type. + */ +typeName: elementaryTypeName[true] | functionTypeName | mappingType | userDefinedTypeName | typeName LBrack expression? RBrack; +elementaryTypeName[boolean allowAddressPayable]: Address | {$allowAddressPayable}? Address Payable | Bool | String | Bytes | SignedIntegerType | UnsignedIntegerType | FixedBytes | Fixed | Ufixed; +functionTypeName +locals [boolean visibilitySet = false, boolean mutabilitySet = false] +: + Function LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)?; + +/** + * The declaration of a single variable. + */ +variableDeclaration: type=typeName location=dataLocation? name=identifier; +dataLocation: Memory | Storage | Calldata; + +/** + * Complex expression. + * Can be an index access, an index range access, a member access, a function call (with optional function call options), + * a type conversion, an unary or binary expression, a comparison or assignment, a ternary expression, + * a new-expression (i.e. a contract creation or the allocation of a dynamic memory array), + * a tuple, an inline array or a primary expression (i.e. an identifier, literal or type name). + */ +expression: + expression LBrack index=expression? RBrack # IndexAccess + | expression LBrack start=expression? Colon end=expression? RBrack # IndexRangeAccess + | expression Period (identifier | Address) # MemberAccess + | expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions + | expression callArgumentList # FunctionCall + | Payable callArgumentList # PayableConversion + | Type LParen typeName RParen # MetaType + | (Inc | Dec | Not | BitNot | Delete | Sub) expression # UnaryPrefixOperation + | expression (Inc | Dec) # UnarySuffixOperation + | expression Exp expression # ExpOperation + | expression (Mul | Div | Mod) expression # MulDivModOperation + | expression (Add | Sub) expression # AddSubOperation + | expression (Shl | Sar | Shr) expression # ShiftOperation + | expression BitAnd expression # BitAndOperation + | expression BitXor expression # BitXorOperation + | expression BitOr expression # BitOrOperation + | expression (LessThan | GreaterThan | LessThanOrEqual | GreaterThanOrEqual) expression # OrderComparison + | expression (Equal | NotEqual) expression # EqualityComparison + | expression And expression # AndOperation + | expression Or expression # OrOperation + | expression Conditional expression Colon expression # Conditional + | expression assignOp expression # Assignment + | New typeName # NewExpression + | tupleExpression # Tuple + | inlineArrayExpression # InlineArray + | ( + identifier + | literal + | elementaryTypeName[false] + | userDefinedTypeName + ) # PrimaryExpression +; + +//@doc:inline +assignOp: Assign | AssignBitOr | AssignBitXor | AssignBitAnd | AssignShl | AssignSar | AssignShr | AssignAdd | AssignSub | AssignMul | AssignDiv | AssignMod; +tupleExpression: LParen (expression? ( Comma expression?)* ) RParen; +/** + * An inline array expression denotes a statically sized array of the common type of the contained expressions. + */ +inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; + +/** + * Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements. + */ +identifier: Identifier | From; + +literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral; +booleanLiteral: True | False; +/** + * A full string literal consists of either one or several consecutive quoted strings. + */ +stringLiteral: StringLiteral+; +/** + * A full hex string literal that consists of either one or several consecutive hex strings. + */ +hexStringLiteral: HexString+; +/** + * A full unicode string literal that consists of either one or several consecutive unicode strings. + */ +unicodeStringLiteral: UnicodeStringLiteral+; + +/** + * Number literals can be decimal or hexadecimal numbers with an optional unit. + */ +numberLiteral: (DecimalNumber | HexNumber) NumberUnit?; +/** + * A curly-braced block of statements. Opens its own scope. + */ +block: LBrace statement* RBrace; + +statement: + block + | simpleStatement + | ifStatement + | forStatement + | whileStatement + | doWhileStatement + | continueStatement + | breakStatement + | tryStatement + | returnStatement + | emitStatement + | assemblyStatement +; + +//@doc:inline +simpleStatement: variableDeclarationStatement | expressionStatement; +/** + * If statement with optional else part. + */ +ifStatement: If LParen expression RParen statement (Else statement)?; +/** + * For statement with optional init, condition and post-loop part. + */ +forStatement: For LParen (simpleStatement | Semicolon) (expressionStatement | Semicolon) expression? RParen statement; +whileStatement: While LParen expression RParen statement; +doWhileStatement: Do statement While LParen expression RParen Semicolon; +/** + * A continue statement. Only allowed inside for, while or do-while loops. + */ +continueStatement: Continue Semicolon; +/** + * A break statement. Only allowed inside for, while or do-while loops. + */ +breakStatement: Break Semicolon; +/** + * A try statement. The contained expression needs to be an external function call or a contract creation. + */ +tryStatement: Try expression (Returns LParen returnParameters=parameterList RParen)? block catchClause+; +/** + * The catch clause of a try statement. + */ +catchClause: Catch (identifier? LParen (arguments=parameterList) RParen)? block; + +returnStatement: Return expression? Semicolon; +/** + * An emit statement. The contained expression needs to refer to an event. + */ +emitStatement: Emit expression callArgumentList Semicolon; +/** + * An inline assembly block. + * The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and + * allowed identifiers is different inside an inline assembly block. + */ +assemblyStatement: Assembly AssemblyDialect? AssemblyLBrace yulStatement* YulRBrace; + +//@doc:inline +variableDeclarationList: variableDeclarations+=variableDeclaration (Comma variableDeclarations+=variableDeclaration)*; +/** + * A tuple of variable names to be used in variable declarations. + * May contain empty fields. + */ +variableDeclarationTuple: + LParen + (Comma* variableDeclarations+=variableDeclaration) + (Comma (variableDeclarations+=variableDeclaration)?)* + RParen; +/** + * A variable declaration statement. + * A single variable may be declared without initial value, whereas a tuple of variables can only be + * declared with initial value. + */ +variableDeclarationStatement: ((variableDeclaration (Assign expression)?) | (variableDeclarationTuple Assign expression)) Semicolon; +expressionStatement: expression Semicolon; + +mappingType: Mapping LParen key=mappingKeyType DoubleArrow value=typeName RParen; +/** + * Only elementary types or user defined types are viable as mapping keys. + */ +mappingKeyType: elementaryTypeName[false] | userDefinedTypeName; + +/** + * A Yul statement within an inline assembly block. + * continue and break statements are only valid within for loops. + * leave statements are only valid within function bodies. + */ +yulStatement: + yulBlock + | yulVariableDeclaration + | yulAssignment + | yulFunctionCall + | yulIfStatement + | yulForStatement + | yulSwitchStatement + | YulLeave + | YulBreak + | YulContinue + | yulFunctionDefinition; + +yulBlock: YulLBrace yulStatement* YulRBrace; + +/** + * The declaration of one or more Yul variables with optional initial value. + * If multiple variables are declared, only a function call is a valid initial value. + */ +yulVariableDeclaration: + (YulLet variables+=YulIdentifier (YulAssign yulExpression)?) + | (YulLet variables+=YulIdentifier (YulComma variables+=YulIdentifier)* (YulAssign yulFunctionCall)?); + +/** + * Any expression can be assigned to a single Yul variable, whereas + * multi-assignments require a function call on the right-hand side. + */ +yulAssignment: yulPath YulAssign yulExpression | (yulPath (YulComma yulPath)+) YulAssign yulFunctionCall; + +yulIfStatement: YulIf cond=yulExpression body=yulBlock; + +yulForStatement: YulFor init=yulBlock cond=yulExpression post=yulBlock body=yulBlock; + +//@doc:inline +yulSwitchCase: YulCase yulLiteral yulBlock; +/** + * A Yul switch statement can consist of only a default-case (deprecated) or + * one or more non-default cases optionally followed by a default-case. + */ +yulSwitchStatement: + YulSwitch yulExpression + ( + (yulSwitchCase+ (YulDefault yulBlock)?) + | (YulDefault yulBlock) + ); + +yulFunctionDefinition: + YulFunction YulIdentifier + YulLParen (arguments+=YulIdentifier (YulComma arguments+=YulIdentifier)*)? YulRParen + (YulArrow returnParameters+=YulIdentifier (YulComma returnParameters+=YulIdentifier)*)? + body=yulBlock; + +/** + * While only identifiers without dots can be declared within inline assembly, + * paths containing dots can refer to declarations outside the inline assembly block. + */ +yulPath: YulIdentifier (YulPeriod YulIdentifier)*; +/** + * A call to a function with return values can only occur as right-hand side of an assignment or + * a variable declaration. + */ +yulFunctionCall: (YulIdentifier | YulEVMBuiltin) YulLParen (yulExpression (YulComma yulExpression)*)? YulRParen; +yulBoolean: YulTrue | YulFalse; +yulLiteral: YulDecimalNumber | YulStringLiteral | YulHexNumber | yulBoolean; +yulExpression: yulPath | yulFunctionCall | yulLiteral; diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 new file mode 100644 index 000000000..c70a99d91 --- /dev/null +++ b/docs/grammar/SolidityLexer.g4 @@ -0,0 +1,334 @@ +lexer grammar SolidityLexer; + +/** + * Keywords reserved for future use in Solidity. + */ +ReservedKeywords: + 'after' | 'alias' | 'apply' | 'auto' | 'case' | 'copyof' | 'default' | 'define' | 'final' + | 'implements' | 'in' | 'inline' | 'let' | 'macro' | 'match' | 'mutable' | 'null' | 'of' + | 'partial' | 'promise' | 'reference' | 'relocatable' | 'sealed' | 'sizeof' | 'static' + | 'supports' | 'switch' | 'typedef' | 'typeof' | 'unchecked' | 'var'; + +Pragma: 'pragma' -> pushMode(PragmaMode); +Abstract: 'abstract'; +Anonymous: 'anonymous'; +Address: 'address'; +As: 'as'; +Assembly: 'assembly' -> pushMode(AssemblyBlockMode); +Bool: 'bool'; +Break: 'break'; +Bytes: 'bytes'; +Calldata: 'calldata'; +Catch: 'catch'; +Constant: 'constant'; +Constructor: 'constructor'; +Continue: 'continue'; +Contract: 'contract'; +Delete: 'delete'; +Do: 'do'; +Else: 'else'; +Emit: 'emit'; +Enum: 'enum'; +Event: 'event'; +External: 'external'; +Fallback: 'fallback'; +False: 'false'; +Fixed: 'fixed' | ('fixed' [0-9]+ 'x' [0-9]+); +From: 'from'; +/** + * Bytes types of fixed length. + * byte is an alias of bytes1. + */ +FixedBytes: + 'byte' | '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'; +For: 'for'; +Function: 'function'; +Hex: 'hex'; +If: 'if'; +Immutable: 'immutable'; +Import: 'import'; +Indexed: 'indexed'; +Interface: 'interface'; +Internal: 'internal'; +Is: 'is'; +Library: 'library'; +Mapping: 'mapping'; +Memory: 'memory'; +Modifier: 'modifier'; +New: 'new'; +/** + * Unit denomination for numbers. + */ +NumberUnit: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'; +Override: 'override'; +Payable: 'payable'; +Private: 'private'; +Public: 'public'; +Pure: 'pure'; +Receive: 'receive'; +Return: 'return'; +Returns: 'returns'; +/** + * Sized signed integer types. + * int is an alias of int256. + */ +SignedIntegerType: + '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'; +Storage: 'storage'; +String: 'string'; +Struct: 'struct'; +True: 'true'; +Try: 'try'; +Type: 'type'; +Ufixed: 'ufixed' | ('ufixed' [0-9]+ 'x' [0-9]+); +/** + * Sized unsigned integer types. + * uint is an alias of uint256. + */ +UnsignedIntegerType: + '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'; +Using: 'using'; +View: 'view'; +Virtual: 'virtual'; +While: 'while'; + +LParen: '('; +RParen: ')'; +LBrack: '['; +RBrack: ']'; +LBrace: '{'; +RBrace: '}'; +Colon: ':'; +Semicolon: ';'; +Period: '.'; +Conditional: '?'; +DoubleArrow: '=>'; +RightArrow: '->'; + +Assign: '='; +AssignBitOr: '|='; +AssignBitXor: '^='; +AssignBitAnd: '&='; +AssignShl: '<<='; +AssignSar: '>>='; +AssignShr: '>>>='; +AssignAdd: '+='; +AssignSub: '-='; +AssignMul: '*='; +AssignDiv: '/='; +AssignMod: '%='; + +Comma: ','; +Or: '||'; +And: '&&'; +BitOr: '|'; +BitXor: '^'; +BitAnd: '&'; +Shl: '<<'; +Sar: '>>'; +Shr: '>>>'; +Add: '+'; +Sub: '-'; +Mul: '*'; +Div: '/'; +Mod: '%'; +Exp: '**'; + +Equal: '=='; +NotEqual: '!='; +LessThan: '<'; +GreaterThan: '>'; +LessThanOrEqual: '<='; +GreaterThanOrEqual: '>='; +Not: '!'; +BitNot: '~'; +Inc: '++'; +Dec: '--'; + +/** + * A single quoted string literal restricted to printable characters. + */ +StringLiteral: '"' DoubleQuotedStringCharacter* '"' | '\'' SingleQuotedStringCharacter* '\''; +/** + * A single non-empty quoted string literal. + */ +NonEmptyStringLiteral: '"' DoubleQuotedStringCharacter+ '"' | '\'' SingleQuotedStringCharacter+ '\''; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment DoubleQuotedStringCharacter: DoubleQuotedPrintable | EscapeSequence; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment SingleQuotedStringCharacter: SingleQuotedPrintable | EscapeSequence; +/** + * Any printable character except single quote or back slash. + */ +fragment SingleQuotedPrintable: [\u0020-\u0026\u0028-\u005B\u005D-\u007E]; +/** + * Any printable character except double quote or back slash. + */ +fragment DoubleQuotedPrintable: [\u0020-\u0021\u0023-\u005B\u005D-\u007E]; +/** + * Escape sequence. + * Apart from common single character escape sequences, line breaks can be escaped + * as well as four hex digit unicode escapes \\uXXXX and two digit hex escape sequences \\xXX are allowed. + */ +fragment EscapeSequence: + '\\' ( + ['"\\bfnrtv\n\r] + | 'u' HexCharacter HexCharacter HexCharacter HexCharacter + | 'x' HexCharacter HexCharacter + ); +/** + * A single quoted string literal allowing arbitrary unicode characters. + */ +UnicodeStringLiteral: + 'unicode"' DoubleQuotedUnicodeStringCharacter* '"' + | 'unicode\'' SingleQuotedUnicodeStringCharacter* '\''; +//@doc:inline +fragment DoubleQuotedUnicodeStringCharacter: ~["\r\n\\] | EscapeSequence; +//@doc:inline +fragment SingleQuotedUnicodeStringCharacter: ~['\r\n\\] | EscapeSequence; + +/** + * Hex strings need to consist of an even number of hex digits that may be grouped using underscores. + */ +HexString: 'hex' (('"' EvenHexDigits? '"') | ('\'' EvenHexDigits? '\'')); +/** + * Hex numbers consist of a prefix and an arbitrary number of hex digits that may be delimited by underscores. + */ +HexNumber: '0' 'x' HexDigits; +//@doc:inline +fragment HexDigits: HexCharacter ('_'? HexCharacter)*; +//@doc:inline +fragment EvenHexDigits: HexCharacter HexCharacter ('_'? HexCharacter HexCharacter)*; +//@doc:inline +fragment HexCharacter: [0-9A-Fa-f]; + +/** + * A decimal number literal consists of decimal digits that may be delimited by underscores and + * an optional positive or negative exponent. + * If the digits contain a decimal point, the literal has fixed point type. + */ +DecimalNumber: (DecimalDigits | (DecimalDigits? '.' DecimalDigits)) ([eE] '-'? DecimalDigits)?; +//@doc:inline +fragment DecimalDigits: [0-9] ('_'? [0-9])* ; + + +/** + * An identifier in solidity has to start with a letter, a dollar-sign or an underscore and + * may additionally contain numbers after the first symbol. + */ +Identifier: IdentifierStart IdentifierPart*; +//@doc:inline +fragment IdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment IdentifierPart: [a-zA-Z0-9$_]; + +WS: [ \t\r\n\u000C]+ -> skip ; +COMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +mode AssemblyBlockMode; + +//@doc:inline +AssemblyDialect: '"evmasm"'; +AssemblyLBrace: '{' -> popMode, pushMode(YulMode); + +AssemblyBlockWS: [ \t\r\n\u000C]+ -> skip ; +AssemblyBlockCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +AssemblyBlockLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode YulMode; + +YulBreak: 'break'; +YulCase: 'case'; +YulContinue: 'continue'; +YulDefault: 'default'; +YulFalse: 'false'; +YulFor: 'for'; +YulFunction: 'function'; +YulIf: 'if'; +YulLeave: 'leave'; +YulLet: 'let'; +YulSwitch: 'switch'; +YulTrue: 'true'; + +/** + * Builtin functions in the EVM Yul dialect. + */ +YulEVMBuiltin: + 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' + | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' + | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' + | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' + | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' + | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' + | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' + | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit'; + +YulLBrace: '{' -> pushMode(YulMode); +YulRBrace: '}' -> popMode; +YulLParen: '('; +YulRParen: ')'; +YulAssign: ':='; +YulPeriod: '.'; +YulComma: ','; +YulArrow: '->'; + +/** + * Yul identifiers consist of letters, dollar signs, underscores and numbers, but may not start with a number. + * In inline assembly there cannot be dots in user-defined identifiers. Instead see yulPath for expressions + * consisting of identifiers with dots. + */ +YulIdentifier: YulIdentifierStart YulIdentifierPart*; +//@doc:inline +fragment YulIdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment YulIdentifierPart: [a-zA-Z0-9$_]; +/** + * Hex literals in Yul consist of a prefix and one or more hexadecimal digits. + */ +YulHexNumber: '0' 'x' [0-9a-fA-F]+; +/** + * Decimal literals in Yul may be zero or any sequence of decimal digits without leading zeroes. + */ +YulDecimalNumber: '0' | ([1-9] [0-9]*); +/** + * String literals in Yul consist of one or more double-quoted or single-quoted strings + * that may contain escape sequences and printable characters except unescaped line breaks or + * unescaped double-quotes or single-quotes, respectively. + */ +YulStringLiteral: + '"' DoubleQuotedStringCharacter* '"' + | '\'' SingleQuotedStringCharacter* '\''; + + +YulWS: [ \t\r\n\u000C]+ -> skip ; +YulCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +YulLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode PragmaMode; + +/** + * Pragma token. Can contain any kind of symbol except a semicolon. + * Note that currently the solidity parser only allows a subset of this. + */ +//@doc:name pragma-token +//@doc:no-diagram +PragmaToken: ~[;]+; +PragmaSemicolon: ';' -> popMode; + +PragmaWS: [ \t\r\n\u000C]+ -> skip ; +PragmaCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +PragmaLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/index.rst b/docs/index.rst index 470fc56fe..2468c4867 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -72,10 +72,10 @@ Community volunteers help translate this documentation into several languages. They have varying degrees of completeness and up-to-dateness. The English version stands as a reference. -* `French `_ (in progress) +* `French `_ (in progress) * `Italian `_ (in progress) * `Japanese `_ -* `Korean `_ (in progress) +* `Korean `_ (in progress) * `Russian `_ (rather outdated) * `Simplified Chinese `_ (in progress) * `Spanish `_ diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index e992e9491..7a5c52d71 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -205,7 +205,7 @@ The following are dependencies for all builds of Solidity: | `cvc4`_ (Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ -.. _cvc4: http://cvc4.cs.stanford.edu/web/ +.. _cvc4: https://cvc4.cs.stanford.edu/web/ .. _Git: https://git-scm.com/download .. _Boost: https://www.boost.org .. _CMake: https://cmake.org/download/ @@ -243,10 +243,10 @@ command-line builds: sudo xcodebuild -license accept -Our OS X build script uses `the Homebrew `_ +Our OS X build script uses `the Homebrew `_ package manager for installing external dependencies. Here's how to `uninstall Homebrew -`_, +`_, if you ever want to start again from scratch. Prerequisites - Windows diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 39454ece1..f2b12e930 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -285,7 +285,7 @@ likely it will be. since it is not up to the submitter of a transaction, but up to the miners to determine in which block the transaction is included. If you want to schedule future calls of your contract, you can use - the `alarm clock `_ or a similar oracle service. + the `alarm clock `_ or a similar oracle service. .. _the-ethereum-virtual-machine: diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index d50ea06a3..6c0dd0a16 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -5,7 +5,7 @@ Layout of a Solidity Source File Source files can contain an arbitrary number of :ref:`contract definitions`, import_ directives, :ref:`pragma directives` and -:ref:`struct` and :ref:`enum` definitions. +:ref:`struct`, :ref:`enum` and :ref:`function` definitions. .. index:: ! license, spdx @@ -22,7 +22,7 @@ Every source file should start with a comment indicating its license: The compiler does not validate that the license is part of the `list allowed by SPDX `_, but -it does include the supplied string in the `bytecode metadata `_. +it does include the supplied string in the :ref:`bytecode metadata `. If you do not want to specify a license or if the source code is not open-source, please use the special value ``UNLICENSED``. diff --git a/docs/metadata.rst b/docs/metadata.rst index 240814adc..bd827a0d8 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -202,6 +202,6 @@ This automatically verifies the metadata since its hash is part of the bytecode. Excess data corresponds to the constructor input data, which should be decoded according to the interface and presented to the user. -In the repository `source-verify `_ +In the repository `sourcify `_ (`npm package `_) you can see example code that shows how to use this feature. diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index acb7a35d3..21e78f2df 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -36,7 +36,7 @@ for the purposes of NatSpec. - For Vyper, use ``"""`` indented to the inner contents with bare comments. See `Vyper - documentation `__. + documentation `__. The following example shows a contract and a function using all available tags. @@ -195,8 +195,8 @@ JSON file as output: } Note that the key by which to find the methods is the function's -canonical signature as defined in the `Contract -ABI `__ and not simply the function's +canonical signature as defined in the :ref:`Contract +ABI ` and not simply the function's name. .. _header-developer-doc: diff --git a/docs/requirements.txt b/docs/requirements.txt index 8f67f9594..65cc192fd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ sphinx_rtd_theme>=0.3.1 pygments-lexer-solidity>=0.5.1 +sphinx-a4doc>=1.2.1 diff --git a/docs/resources.rst b/docs/resources.rst index 02c1930aa..01c4715a1 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -75,25 +75,14 @@ Solidity Integrations * `Vim Solidity `_ Plugin for the Vim editor providing syntax highlighting. - * `Vim Syntastic `_ + * `Vim Syntastic `_ Plugin for the Vim editor providing compile checking. * Visual Studio Code: - * `Visual Studio Code extension `_ + * `Visual Studio Code extension `_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. -Discontinued: - -* `Mix IDE `_ - Qt based IDE for designing, debugging and testing solidity smart contracts. - -* `Ethereum Studio `_ - Specialized web IDE that also provides shell access to a complete Ethereum environment. - -* `Visual Studio Extension `_ - Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. - Solidity Tools ~~~~~~~~~~~~~~ @@ -142,8 +131,5 @@ Solidity Tools Third-Party Solidity Parsers and Grammars ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* `solidity-parser `_ - Solidity parser for JavaScript - -* `Solidity Grammar for ANTLR 4 `_ - Solidity grammar for the ANTLR 4 parser generator +* `Solidity Parser for JavaScript `_ + A Solidity parser for JS built on top of a robust ANTLR4 grammar. diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index a471fdd00..3a6d5dfd6 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -259,7 +259,7 @@ more special edge cases for signed numbers. Try to use ``require`` to limit the size of inputs to a reasonable range and use the :ref:`SMT checker` to find potential overflows, or use a library like -`SafeMath `_ +`SafeMath `_ if you want all overflows to cause a revert. Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect. diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 016130730..80cd5f617 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -43,12 +43,14 @@ visibility. Functions ========= -Functions are the executable units of code within a contract. +Functions are the executable units of code. Functions are usually +defined inside a contract, but they can also be defined outside of +contracts. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >0.7.0 <0.8.0; contract SimpleAuction { function bid() public payable { // Function @@ -56,6 +58,11 @@ Functions are the executable units of code within a contract. } } + // Helper function defined outside of a contract + function helper(uint x) pure returns (uint) { + return x * 2; + } + :ref:`function-calls` can happen internally or externally and have different levels of :ref:`visibility` towards other contracts. :ref:`Functions` accept :ref:`parameters and return variables` to pass parameters diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 30ceec3a9..73a65df08 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -1145,11 +1145,11 @@ NatSpec Solidity contracts can have a form of comments that are the basis of the Ethereum Natural Language Specification Format. -Add comments above functions or contracts following `doxygen `_ notation +Add comments above functions or contracts following `doxygen `_ notation of one or multiple lines starting with ``///`` or a multiline comment starting with ``/**`` and ending with ``*/``. -For example, the contract from `a simple smart contract `_ with the comments +For example, the contract from :ref:`a simple smart contract ` with the comments added looks like the one below:: // SPDX-License-Identifier: GPL-3.0 @@ -1176,6 +1176,6 @@ added looks like the one below:: } } -It is recommended that Solidity contracts are fully annotated using `NatSpec `_ for all public interfaces (everything in the ABI). +It is recommended that Solidity contracts are fully annotated using :ref:`NatSpec ` for all public interfaces (everything in the ABI). -Please see the section about `NatSpec `_ for a detailed explanation. +Please see the section about :ref:`NatSpec ` for a detailed explanation. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a32900a50..b67f84739 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -160,6 +160,8 @@ more details on error handling and when to use which function. .. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, +.. _mathematical-and-cryptographic-functions: + Mathematical and Cryptographic Functions ---------------------------------------- @@ -193,17 +195,17 @@ Mathematical and Cryptographic Functions ``ecrecover`` returns an ``address``, and not an ``address payable``. See :ref:`address payable
` for conversion, in case you need to transfer funds to the recovered address. - For further details, read `example usage `_. + For further details, read `example usage `_. .. warning:: If you use ``ecrecover``, be aware that a valid signature can be turned into a different valid signature without requiring knowledge of the corresponding private key. In the Homestead hard fork, this issue was fixed - for _transaction_ signatures (see `EIP-2 `_), but + for _transaction_ signatures (see `EIP-2 `_), but the ecrecover function remained unchanged. This is usually not a problem unless you require signatures to be unique or - use them to identify items. OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. + use them to identify items. OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. .. note:: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index c1d416dc2..008855179 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -441,11 +441,11 @@ Output Description }, "deployedBytecode": { ..., // The same layout as above. - "immutableReferences": [ + "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": { diff --git a/docs/yul.rst b/docs/yul.rst index 2054f35c7..7d064059b 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -476,9 +476,8 @@ which are explained in their own chapter. TypeName = Identifier TypedIdentifierList = Identifier ( ':' TypeName )? ( ',' Identifier ( ':' TypeName )? )* Literal = - (NumberLiteral | StringLiteral | HexLiteral | TrueLiteral | FalseLiteral) ( ':' TypeName )? + (NumberLiteral | StringLiteral | TrueLiteral | FalseLiteral) ( ':' TypeName )? NumberLiteral = HexNumber | DecimalNumber - HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' TrueLiteral = 'true' FalseLiteral = 'false' @@ -688,8 +687,6 @@ We will use a destructuring notation for the AST nodes. L'[$parami] = vi and L'[$reti] = 0 for all i. Let G'', L'', mode = E(Gn, L', block) G'', Ln, L''[$ret1], ..., L''[$retm] - E(G, L, l: HexLiteral) = G, L, hexString(l), - where hexString decodes l from hex and left-aligns it into 32 bytes E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l), where utf8EncodeLeftAligned performs a utf8 encoding of l and aligns it left into 32 bytes diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index ec6c50702..3741486a9 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -155,8 +155,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool ); break; default: - bool invMem = SemanticInformation::invalidatesMemory(_item.instruction()); - bool invStor = SemanticInformation::invalidatesStorage(_item.instruction()); + bool invMem = + SemanticInformation::memory(_item.instruction()) == SemanticInformation::Write; + bool invStor = + SemanticInformation::storage(_item.instruction()) == SemanticInformation::Write; // We could be a bit more fine-grained here (CALL only invalidates part of // memory, etc), but we do not for now. if (invMem) @@ -420,4 +422,3 @@ KnownState::Id KnownState::tagUnion(set _tags) return id; } } - diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 3d4ead64f..1826aac5f 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -233,7 +233,7 @@ bool SemanticInformation::movable(Instruction _instruction) return true; } -bool SemanticInformation::sideEffectFree(Instruction _instruction) +bool SemanticInformation::canBeRemoved(Instruction _instruction) { // These are not really functional. assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, ""); @@ -241,15 +241,15 @@ bool SemanticInformation::sideEffectFree(Instruction _instruction) return !instructionInfo(_instruction).sideEffects; } -bool SemanticInformation::sideEffectFreeIfNoMSize(Instruction _instruction) +bool SemanticInformation::canBeRemovedIfNoMSize(Instruction _instruction) { if (_instruction == Instruction::KECCAK256 || _instruction == Instruction::MLOAD) return true; else - return sideEffectFree(_instruction); + return canBeRemoved(_instruction); } -bool SemanticInformation::invalidatesMemory(Instruction _instruction) +SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction) { switch (_instruction) { @@ -263,13 +263,47 @@ bool SemanticInformation::invalidatesMemory(Instruction _instruction) case Instruction::CALLCODE: case Instruction::DELEGATECALL: case Instruction::STATICCALL: - return true; + return SemanticInformation::Write; + + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::KECCAK256: + case Instruction::MLOAD: + case Instruction::MSIZE: + case Instruction::RETURN: + case Instruction::REVERT: + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + return SemanticInformation::Read; + default: - return false; + return SemanticInformation::None; } } -bool SemanticInformation::invalidatesStorage(Instruction _instruction) +bool SemanticInformation::movableApartFromEffects(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::EXTCODEHASH: + case Instruction::EXTCODESIZE: + case Instruction::RETURNDATASIZE: + case Instruction::BALANCE: + case Instruction::SELFBALANCE: + case Instruction::SLOAD: + case Instruction::KECCAK256: + case Instruction::MLOAD: + return true; + + default: + return movable(_instruction); + } +} + +SemanticInformation::Effect SemanticInformation::storage(Instruction _instruction) { switch (_instruction) { @@ -279,9 +313,45 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) case Instruction::CREATE: case Instruction::CREATE2: case Instruction::SSTORE: - return true; + return SemanticInformation::Write; + + case Instruction::SLOAD: + case Instruction::STATICCALL: + return SemanticInformation::Read; + default: - return false; + return SemanticInformation::None; + } +} + +SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::SELFDESTRUCT: + case Instruction::STATICCALL: // because it can affect returndatasize + // Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they + // are just marked as having 'other side effects.' + return SemanticInformation::Write; + + case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: + case Instruction::RETURNDATASIZE: + case Instruction::BALANCE: + case Instruction::SELFBALANCE: + case Instruction::RETURNDATACOPY: + case Instruction::EXTCODECOPY: + // PC and GAS are specifically excluded here. Instructions such as CALLER, CALLVALUE, + // ADDRESS are excluded because they cannot change during execution. + return SemanticInformation::Read; + + default: + return SemanticInformation::None; } } diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index f919b4207..679766e66 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -36,6 +36,15 @@ class AssemblyItem; */ struct SemanticInformation { + /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory + /// and other blockchain state). + enum Effect + { + None, + Read, + Write + }; + /// @returns true if the given items starts a new block for common subexpression analysis. /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize. static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant); @@ -57,20 +66,23 @@ struct SemanticInformation /// without altering the semantics. This means it cannot depend on storage or memory, /// cannot have any side-effects, but it can depend on a call-constant state of the blockchain. static bool movable(Instruction _instruction); + /// If true, the expressions in this code can be moved or copied (together with their arguments) + /// across control flow branches and instructions as long as these instructions' 'effects' do + /// not influence the 'effects' of the aforementioned expressions. + static bool movableApartFromEffects(Instruction _instruction); /// @returns true if the instruction can be removed without changing the semantics. /// This does not mean that it has to be deterministic or retrieve information from /// somewhere else than purely the values of its arguments. - static bool sideEffectFree(Instruction _instruction); + static bool canBeRemoved(Instruction _instruction); /// @returns true if the instruction can be removed without changing the semantics. /// This does not mean that it has to be deterministic or retrieve information from /// somewhere else than purely the values of its arguments. /// If true, the instruction is still allowed to influence the value returned by the /// msize instruction. - static bool sideEffectFreeIfNoMSize(Instruction _instruction); - /// @returns true if the given instruction modifies memory. - static bool invalidatesMemory(Instruction _instruction); - /// @returns true if the given instruction modifies storage (even indirectly). - static bool invalidatesStorage(Instruction _instruction); + static bool canBeRemovedIfNoMSize(Instruction _instruction); + static Effect memory(Instruction _instruction); + static Effect storage(Instruction _instruction); + static Effect otherState(Instruction _instruction); static bool invalidInPureFunctions(Instruction _instruction); static bool invalidInViewFunctions(Instruction _instruction); }; diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 29a20a8d8..02a983651 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using namespace std; diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index 19c4d187c..089f606fa 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -94,6 +94,8 @@ void ParserBase::expectToken(Token _value, bool _advance) void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentNodeName, bool _advance) { + solAssert(m_inParserRecovery, "The function is supposed to be called during parser recovery only."); + Token tok = m_scanner->currentToken(); if (tok != _value) { @@ -103,24 +105,20 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN m_scanner->next(); string const expectedToken = ParserBase::tokenName(_value); - string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; if (m_scanner->currentToken() == Token::EOS) { // rollback to where the token started, and raise exception to be caught at a higher level. m_scanner->setPosition(static_cast(startPosition)); - m_inParserRecovery = true; + string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; fatalParserError(1957_error, errorLoc, msg); } else { - if (m_inParserRecovery) - parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); - else - parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken); + parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); m_inParserRecovery = false; } } - else if (m_inParserRecovery) + else { string expectedToken = ParserBase::tokenName(_value); parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 017a65e18..1a3903d5c 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -540,7 +540,7 @@ void Scanner::scanToken() if (m_char == '=') token = selectToken(Token::Equal); else if (m_char == '>') - token = selectToken(Token::Arrow); + token = selectToken(Token::DoubleArrow); else token = Token::Assign; break; @@ -563,12 +563,14 @@ void Scanner::scanToken() token = Token::Add; break; case '-': - // - -- -= + // - -- -= -> advance(); if (m_char == '-') token = selectToken(Token::Dec); else if (m_char == '=') token = selectToken(Token::AssignSub); + else if (m_char == '>') + token = selectToken(Token::RightArrow); else token = Token::Sub; break; @@ -678,7 +680,7 @@ void Scanner::scanToken() else token = setError(ScannerError::IllegalToken); } - else if (token == Token::Unicode) + else if (token == Token::Unicode && m_kind != ScannerKind::Yul) { // reset m = 0; @@ -967,7 +969,17 @@ tuple Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char) || (m_char == '.' && m_kind == ScannerKind::Yul)) addLiteralCharAndAdvance(); literal.complete(); - return TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); + auto const token = TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); + if (m_kind == ScannerKind::Yul) + { + // Turn Solidity identifier into a Yul keyword + if (m_tokens[NextNext].literal == "leave") + return std::make_tuple(Token::Leave, 0, 0); + // Turn non-Yul keywords into identifiers. + if (!TokenTraits::isYulKeyword(std::get<0>(token))) + return std::make_tuple(Token::Identifier, 0, 0); + } + return token; } } // namespace solidity::langutil diff --git a/liblangutil/Token.cpp b/liblangutil/Token.cpp index 70b645992..bbcfe1590 100644 --- a/liblangutil/Token.cpp +++ b/liblangutil/Token.cpp @@ -152,6 +152,11 @@ static Token keywordByName(string const& _name) return it == keywords.end() ? Token::Identifier : it->second; } +bool isYulKeyword(string const& _literal) +{ + return _literal == "leave" || isYulKeyword(keywordByName(_literal)); +} + tuple fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 366c354cd..3d9c71ab3 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -83,7 +83,8 @@ namespace solidity::langutil T(Semicolon, ";", 0) \ T(Period, ".", 0) \ T(Conditional, "?", 3) \ - T(Arrow, "=>", 0) \ + T(DoubleArrow, "=>", 0) \ + T(RightArrow, "->", 0) \ \ /* Assignment operators. */ \ /* IsAssignmentOp() relies on this block of enum values being */ \ @@ -268,6 +269,9 @@ namespace solidity::langutil K(Unchecked, "unchecked", 0) \ K(Var, "var", 0) \ \ + /* Yul-specific tokens, but not keywords. */ \ + T(Leave, "leave", 0) \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -316,6 +320,15 @@ namespace TokenTraits constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } constexpr bool isReservedKeyword(Token op) { return (Token::After <= op && op <= Token::Unchecked); } + constexpr bool isYulKeyword(Token tok) + { + return tok == Token::Function || tok == Token::Let || tok == Token::If || tok == Token::Switch || tok == Token::Case || + tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave || + tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; + } + + bool isYulKeyword(std::string const& _literal); + inline Token AssignmentToBinaryOp(Token op) { solAssert(isAssignmentOp(op) && op != Token::Assign, ""); diff --git a/libsmtutil/CVC4Interface.cpp b/libsmtutil/CVC4Interface.cpp index 2a795d827..40526306f 100644 --- a/libsmtutil/CVC4Interface.cpp +++ b/libsmtutil/CVC4Interface.cpp @@ -188,8 +188,14 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]); else if (n == "mod") return m_context.mkExpr(CVC4::kind::INTS_MODULUS, arguments[0], arguments[1]); + else if (n == "bvnot") + return m_context.mkExpr(CVC4::kind::BITVECTOR_NOT, arguments[0]); else if (n == "bvand") return m_context.mkExpr(CVC4::kind::BITVECTOR_AND, arguments[0], arguments[1]); + else if (n == "bvor") + return m_context.mkExpr(CVC4::kind::BITVECTOR_OR, arguments[0], arguments[1]); + else if (n == "bvxor") + return m_context.mkExpr(CVC4::kind::BITVECTOR_XOR, arguments[0], arguments[1]); else if (n == "int2bv") { size_t size = std::stoul(_expr.arguments[1].name); diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index d4c4d7fef..c45ebd77b 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -95,7 +95,10 @@ public: {"*", 2}, {"/", 2}, {"mod", 2}, + {"bvnot", 1}, {"bvand", 2}, + {"bvor", 2}, + {"bvxor", 2}, {"int2bv", 2}, {"bv2int", 1}, {"select", 2}, @@ -286,11 +289,26 @@ public: auto intSort = _a.sort; return Expression("mod", {std::move(_a), std::move(_b)}, intSort); } + friend Expression operator~(Expression _a) + { + auto bvSort = _a.sort; + return Expression("bvnot", {std::move(_a)}, bvSort); + } friend Expression operator&(Expression _a, Expression _b) { auto bvSort = _a.sort; return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort); } + friend Expression operator^(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort); + } + friend Expression operator|(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort); + } Expression operator()(std::vector _arguments) const { smtAssert( diff --git a/libsmtutil/Z3Interface.cpp b/libsmtutil/Z3Interface.cpp index 501446553..909a5cde2 100644 --- a/libsmtutil/Z3Interface.cpp +++ b/libsmtutil/Z3Interface.cpp @@ -181,8 +181,14 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] / arguments[1]; else if (n == "mod") return z3::mod(arguments[0], arguments[1]); + else if (n == "bvnot") + return ~arguments[0]; else if (n == "bvand") return arguments[0] & arguments[1]; + else if (n == "bvor") + return arguments[0] | arguments[1]; + else if (n == "bvxor") + return arguments[0] ^ arguments[1]; else if (n == "int2bv") { size_t size = std::stoul(_expr.arguments[1].name); diff --git a/libsmtutil/Z3Interface.h b/libsmtutil/Z3Interface.h index 22f793337..23534fd75 100644 --- a/libsmtutil/Z3Interface.h +++ b/libsmtutil/Z3Interface.h @@ -49,9 +49,7 @@ public: // Z3 "basic resources" limit. // This is used to make the runs more deterministic and platform/machine independent. - // The tests start failing for Z3 with less than 10000000, - // so using double that. - static int const resourceLimit = 20000000; + static int const resourceLimit = 12500000; private: void declareFunction(std::string const& _name, Sort const& _sort); diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 885ae49a7..dee7e8220 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -134,15 +134,26 @@ vector GlobalContext::declarations() const MagicVariableDeclaration const* GlobalContext::currentThis() const { if (!m_thisPointer[m_currentContract]) - m_thisPointer[m_currentContract] = make_shared(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract); + m_thisPointer[m_currentContract] = + make_shared(magicVariableToID("this"), "this", type); + } return m_thisPointer[m_currentContract].get(); - } MagicVariableDeclaration const* GlobalContext::currentSuper() const { if (!m_superPointer[m_currentContract]) - m_superPointer[m_currentContract] = make_shared(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract, true); + m_superPointer[m_currentContract] = + make_shared(magicVariableToID("super"), "super", type); + } return m_superPointer[m_currentContract].get(); } diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h index b84e3a026..191359c60 100644 --- a/libsolidity/analysis/GlobalContext.h +++ b/libsolidity/analysis/GlobalContext.h @@ -46,6 +46,7 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + void resetCurrentContract() { m_currentContract = nullptr; } MagicVariableDeclaration const* currentThis() const; MagicVariableDeclaration const* currentSuper() const; diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 5af640e58..93f676c6f 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -34,10 +34,12 @@ void ImmutableValidator::analyze() for (ContractDefinition const* contract: linearizedContracts) for (VariableDeclaration const* stateVar: contract->stateVariables()) if (stateVar->value()) - { + m_initializedStateVariables.emplace(stateVar); + + 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()) @@ -163,41 +165,44 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va if (!_variableReference.isStateVariable() || !_variableReference.immutable()) return; - if (_expression.annotation().willBeWrittenTo && _expression.annotation().lValueOfOrdinaryAssignment) + // If this is not an ordinary assignment, we write and read at the same time. + bool write = _expression.annotation().willBeWrittenTo; + bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment; + if (write) { if (!m_currentConstructor) m_errorReporter.typeError( 1581_error, _expression.location(), - "Immutable variables can only be initialized inline or assigned directly in the constructor." + "Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor." ); else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) m_errorReporter.typeError( 7484_error, _expression.location(), - "Immutable variables must be initialized in the constructor of the contract they are defined in." + "Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in." ); else if (m_inLoop) m_errorReporter.typeError( 6672_error, _expression.location(), - "Immutable variables can only be initialized once, not in a while statement." + "Cannot write to immutable here: Immutable variables cannot be initialized inside a loop." ); else if (m_inBranch) m_errorReporter.typeError( 4599_error, _expression.location(), - "Immutable variables must be initialized unconditionally, not in an if statement." + "Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement." ); - - if (!m_initializedStateVariables.emplace(&_variableReference).second) + else if (m_initializedStateVariables.count(&_variableReference)) m_errorReporter.typeError( 1574_error, _expression.location(), "Immutable state variable already initialized." ); + m_initializedStateVariables.emplace(&_variableReference); } - else if (m_inConstructionContext) + if (read && m_inConstructionContext) m_errorReporter.typeError( 7733_error, _expression.location(), diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index c7be364a3..957046810 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -275,6 +275,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!resolveNamesAndTypesInternal(*node, true)) success = false; } + + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); + m_globalContext.resetCurrentContract(); + return success; } else @@ -548,6 +554,10 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) void DeclarationRegistrationHelper::endVisit(ContractDefinition&) { + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); + m_globalContext.resetCurrentContract(); m_currentContract = nullptr; closeCurrentScope(); } diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index cb04eec6f..47be22810 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -277,7 +277,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker bool visit(FunctionCall const& _functionCall) override { - if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return true; if (FunctionTypePointer const functionType = dynamic_cast(_functionCall.expression().annotation().type)) diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 208749de9..75445f229 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -325,6 +325,17 @@ void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _docum vector path; boost::split(path, name, boost::is_any_of(".")); + if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); })) + { + m_errorReporter.docstringParsingError( + 5967_error, + _documentation.location(), + "Documentation tag @inheritdoc reference \"" + + name + + "\" is malformed." + ); + return; + } Declaration const* result = m_resolver.pathFromCurrentScope(path); if (result == nullptr) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 68f52ac26..a6d797910 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -292,7 +292,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation) bool StaticAnalyzer::visit(FunctionCall const& _functionCall) { - if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall) { auto functionType = dynamic_cast(_functionCall.expression().annotation().type); solAssert(functionType, ""); @@ -311,6 +311,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) ); } if ( + m_currentContract && m_currentContract->isLibrary() && functionType->kind() == FunctionType::Kind::DelegateCall && functionType->declaration().scope() == m_currentContract diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c0380ffe5..670939321 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -295,7 +295,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(ContractDefinition const& _contract) { - m_isInterface = _contract.isInterface(); + m_currentContractKind = _contract.contractKind(); ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) @@ -309,19 +309,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) return true; } +void SyntaxChecker::endVisit(ContractDefinition const&) +{ + m_currentContractKind = std::nullopt; +} + bool SyntaxChecker::visit(FunctionDefinition const& _function) { - if (!_function.isConstructor() && _function.noVisibilitySpecified()) + solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); + + if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified()) { - string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; + string suggestedVisibility = + _function.isFallback() || + _function.isReceive() || + m_currentContractKind == ContractKind::Interface + ? "external" : "public"; m_errorReporter.syntaxError( 4937_error, _function.location(), "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" ); } + else if (_function.isFree()) + { + if (!_function.noVisibilitySpecified()) + m_errorReporter.syntaxError( + 4126_error, + _function.location(), + "Free functions cannot have visibility." + ); + if (!_function.isImplemented()) + m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented."); + } - if (m_isInterface && !_function.modifiers().empty()) + if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty()) m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers."); else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); @@ -342,23 +364,6 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node) return true; } -bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) -{ - // Report if none of the variable components in the tuple have a name (only possible via deprecated "var") - if (std::all_of( - _statement.declarations().begin(), - _statement.declarations().end(), - [](ASTPointer const& declaration) { return declaration == nullptr; } - )) - m_errorReporter.syntaxError( - 3299_error, - _statement.location(), - "The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty." - ); - - return true; -} - bool SyntaxChecker::visit(StructDefinition const& _struct) { if (_struct.members().empty()) diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 1ace2f36a..52c82ff6e 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -83,11 +83,10 @@ private: bool visit(PlaceholderStatement const& _placeholderStatement) override; bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; bool visit(FunctionDefinition const& _function) override; bool visit(FunctionTypeName const& _node) override; - bool visit(VariableDeclarationStatement const& _statement) override; - bool visit(StructDefinition const& _struct) override; bool visit(Literal const& _literal) override; @@ -102,7 +101,7 @@ private: bool m_versionPragmaFound = false; int m_inLoopDepth = 0; - bool m_isInterface = false; + std::optional m_currentContractKind; SourceUnit const* m_sourceUnit = nullptr; }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f0b174d19..990192d68 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -271,6 +271,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); + solAssert(m_currentContract, ""); if (m_currentContract->isInterface() && !base->isInterface()) m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); @@ -327,7 +328,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) { if (_function.markedVirtual()) { - if (_function.isConstructor()) + if (_function.isFree()) + m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual."); + else if (_function.isConstructor()) m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); else if (_function.annotation().contract->isInterface()) m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); @@ -336,12 +339,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); } + if (_function.overrides() && _function.isFree()) + m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override."); if (_function.isPayable()) { if (_function.libraryFunction()) m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); - if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) + else if (_function.isFree()) + m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable."); + else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); } @@ -415,9 +422,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function) set modifiers; for (ASTPointer const& modifier: _function.modifiers()) { - auto baseContracts = dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts; - // Delete first base which is just the main contract itself - baseContracts.erase(baseContracts.begin()); + vector baseContracts; + if (auto contract = dynamic_cast(_function.scope())) + { + baseContracts = contract->annotation().linearizedBaseContracts; + // Delete first base which is just the main contract itself + baseContracts.erase(baseContracts.begin()); + } visitManually( *modifier, @@ -432,7 +443,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_currentContract->isInterface()) + + solAssert(_function.isFree() == !m_currentContract, ""); + if (!m_currentContract) + { + solAssert(!_function.isConstructor(), ""); + solAssert(!_function.isFallback(), ""); + solAssert(!_function.isReceive(), ""); + } + else if (m_currentContract->isInterface()) { if (_function.isImplemented()) m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); @@ -445,6 +464,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (m_currentContract->contractKind() == ContractKind::Library) if (_function.isConstructor()) m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); + if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) @@ -452,7 +472,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); else if (!_function.virtualSemantics()) - m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + { + if (_function.isFree()) + solAssert(m_errorReporter.hasErrors(), ""); + else + m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + } if (_function.isFallback()) @@ -591,7 +616,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (_variable.isStateVariable()) m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true)); else - m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->canonicalName(), false)); + m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->toString(true), false)); } vector oversizedSubtypes = frontend::oversizedSubtypes(*varType); for (Type const* subtype: oversizedSubtypes) @@ -885,7 +910,7 @@ bool TypeChecker::visit(IfStatement const& _ifStatement) void TypeChecker::endVisit(TryStatement const& _tryStatement) { FunctionCall const* externalCall = dynamic_cast(&_tryStatement.externalCall()); - if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall) + if (!externalCall || *externalCall->annotation().kind != FunctionCallKind::FunctionCall) { m_errorReporter.typeError( 5347_error, @@ -1095,7 +1120,7 @@ void TypeChecker::endVisit(Return const& _return) void TypeChecker::endVisit(EmitStatement const& _emit) { if ( - _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + *_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || type(_emit.eventCall().expression())->category() != Type::Category::Function || dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event ) @@ -1591,7 +1616,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( FunctionCall const& _functionCall ) { - solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(*_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); TypePointer const& expressionType = type(_functionCall.expression()); vector> const& arguments = _functionCall.arguments(); @@ -1726,7 +1751,9 @@ void TypeChecker::typeCheckFunctionCall( if (_functionType->kind() == FunctionType::Kind::Declaration) { + solAssert(_functionType->declaration().annotation().contract, ""); if ( + m_currentContract && m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && !dynamic_cast(_functionType->declaration()).isImplemented() ) @@ -1956,8 +1983,10 @@ void TypeChecker::typeCheckFunctionGeneralChecks( bool const isPositionalCall = _functionCall.names().empty(); bool const isVariadic = _functionType->takesArbitraryParameters(); + auto functionCallKind = *_functionCall.annotation().kind; + solAssert( - !isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall, + !isVariadic || functionCallKind == FunctionCallKind::FunctionCall, "Struct constructor calls cannot be variadic." ); @@ -1972,7 +2001,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( ) { bool const isStructConstructorCall = - _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + functionCallKind == FunctionCallKind::StructConstructorCall; auto [errorId, description] = [&]() -> tuple { string msg = isVariadic ? @@ -2235,13 +2264,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) default: m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable"); - funcCallAnno.kind = FunctionCallKind::Unset; + // Unreachable, because fatalTypeError throws. We don't set kind, but that's okay because the switch below + // is never reached. And, even if it was, SetOnce would trigger an assertion violation and not UB. funcCallAnno.isPure = argumentsArePure; break; } // Determine return types - switch (funcCallAnno.kind) + switch (*funcCallAnno.kind) { case FunctionCallKind::TypeConversion: funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); @@ -2291,7 +2321,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } - case FunctionCallKind::Unset: // fall-through default: // for non-callables, ensure error reported and annotate node to void function solAssert(m_errorReporter.hasErrors(), ""); @@ -2452,18 +2481,23 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (contract->abstract()) m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); - solAssert(!!m_currentContract, ""); - m_currentContract->annotation().contractDependencies.insert(contract); - solAssert( - !contract->annotation().linearizedBaseContracts.empty(), - "Linearized base contracts not yet available." - ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4579_error, - _newExpression.location(), - "Circular reference for contract creation (cannot create instance of derived or same contract)." + if (m_currentContract) + { + // TODO this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + m_currentContract->annotation().contractDependencies.insert(contract); + solAssert( + !contract->annotation().linearizedBaseContracts.empty(), + "Linearized base contracts not yet available." ); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4579_error, + _newExpression.location(), + "Circular reference for contract creation (cannot create instance of derived or same contract)." + ); + } _newExpression.annotation().type = FunctionType::newExpressionType(*contract); } @@ -2505,7 +2539,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) // Retrieve the types of the arguments if this is used to call a function. auto const& arguments = _memberAccess.annotation().arguments; - MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName); + MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName); size_t const initialMemberCount = possibleMembers.size(); if (initialMemberCount > 1 && arguments) { @@ -2531,7 +2565,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) DataLocation::Storage, exprType ); - if (!storageType->members(m_currentContract).membersByName(memberName).empty()) + if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty()) m_errorReporter.fatalTypeError( 4994_error, _memberAccess.location(), @@ -2689,8 +2723,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) { annotation.isPure = true; ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); - m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); - if ( memberName == "runtimeCode" && !accessedContractType.immutableVariables().empty() @@ -2701,12 +2733,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "\"runtimeCode\" is not available for contracts containing immutable variables." ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4224_error, - _memberAccess.location(), - "Circular reference for contract code access." - ); + if (m_currentContract) + { + // TODO in the same way as with ``new``, + // this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + + m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4224_error, + _memberAccess.location(), + "Circular reference for contract code access." + ); + } } else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name") annotation.isPure = true; @@ -3107,6 +3148,16 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.annotation().isPure = true; } +void TypeChecker::endVisit(UsingForDirective const& _usingFor) +{ + if (m_currentContract->isInterface()) + m_errorReporter.typeError( + 9088_error, + _usingFor.location(), + "The \"using for\" directive is not allowed inside interfaces." + ); +} + bool TypeChecker::contractDependenciesAreCyclic( ContractDefinition const& _contract, std::set const& _seenContracts diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index e34c9e9d0..70041766d 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -143,6 +143,7 @@ private: bool visit(Identifier const& _identifier) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; + void endVisit(UsingForDirective const& _usingForDirective) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, @@ -167,6 +168,16 @@ private: bool experimentalFeatureActive(ExperimentalFeature _feature) const; + /// @returns the current scope that can have function or type definitions. + /// This is either a contract or a source unit. + ASTNode const* currentDefinitionScope() const + { + if (m_currentContract) + return m_currentContract; + else + return m_currentSourceUnit; + } + SourceUnit const* m_currentSourceUnit = nullptr; ContractDefinition const* m_currentContract = nullptr; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 469d399b1..b75b4a240 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -128,28 +128,12 @@ private: bool ViewPureChecker::check() { - vector contracts; - - for (auto const& node: m_ast) - { - SourceUnit const* source = dynamic_cast(node.get()); - solAssert(source, ""); - contracts += source->filteredNodes(source->nodes()); - } - - // Check modifiers first to infer their state mutability. - for (auto const& contract: contracts) - for (ModifierDefinition const* mod: contract->functionModifiers()) - mod->accept(*this); - - for (auto const& contract: contracts) - contract->accept(*this); + for (auto const& source: m_ast) + source->accept(*this); return !m_errors; } - - bool ViewPureChecker::visit(FunctionDefinition const& _funDef) { solAssert(!m_currentFunction, ""); @@ -304,12 +288,31 @@ void ViewPureChecker::reportMutability( m_currentFunction->stateMutability() == StateMutability::Pure || m_currentFunction->stateMutability() == StateMutability::NonPayable, "" - ); + ); +} + +ViewPureChecker::MutabilityAndLocation const& ViewPureChecker::modifierMutability( + ModifierDefinition const& _modifier +) +{ + if (!m_inferredMutability.count(&_modifier)) + { + MutabilityAndLocation bestMutabilityAndLocation{}; + FunctionDefinition const* currentFunction = nullptr; + swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation); + swap(currentFunction, m_currentFunction); + + _modifier.accept(*this); + + swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation); + swap(currentFunction, m_currentFunction); + } + return m_inferredMutability.at(&_modifier); } void ViewPureChecker::endVisit(FunctionCall const& _functionCall) { - if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return; StateMutability mutability = dynamic_cast(*_functionCall.expression().annotation().type).stateMutability(); @@ -429,8 +432,7 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) solAssert(_modifier.name(), ""); if (ModifierDefinition const* mod = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) { - solAssert(m_inferredMutability.count(mod), ""); - auto const& mutAndLocation = m_inferredMutability.at(mod); + MutabilityAndLocation const& mutAndLocation = modifierMutability(*mod); reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location); } else diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 87135b4f7..dfbb2f747 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -71,6 +71,9 @@ private: std::optional const& _nestedLocation = {} ); + /// Determines the mutability of modifier if not already cached. + MutabilityAndLocation const& modifierMutability(ModifierDefinition const& _modifier); + std::vector> const& m_ast; langutil::ErrorReporter& m_errorReporter; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 662ea81d3..cac28eac0 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -292,7 +292,7 @@ bool FunctionDefinition::libraryFunction() const Visibility FunctionDefinition::defaultVisibility() const { solAssert(!isConstructor(), ""); - return Declaration::defaultVisibility(); + return isFree() ? Visibility::Internal : Declaration::defaultVisibility(); } FunctionTypePointer FunctionDefinition::functionType(bool _internal) const @@ -338,7 +338,7 @@ TypePointer FunctionDefinition::type() const TypePointer FunctionDefinition::typeViaContractName() const { - if (annotation().contract->isLibrary()) + if (libraryFunction()) { if (isPublic()) return FunctionType(*this).asExternallyCallableFunction(true); @@ -374,7 +374,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual( if (_searchStart == nullptr && !virtualSemantics()) return *this; - solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + solAssert(!isFree(), ""); + solAssert(isOrdinary(), ""); + solAssert(!libraryFunction(), ""); FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false); @@ -603,9 +605,8 @@ bool VariableDeclaration::isLibraryFunctionParameter() const if (!isCallableOrCatchParameter()) return false; if (auto const* funDef = dynamic_cast(scope())) - return dynamic_cast(*funDef->scope()).isLibrary(); - else - return false; + return funDef->libraryFunction(); + return false; } bool VariableDeclaration::isEventParameter() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 28d71c2b7..cfa438ce5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -774,6 +774,7 @@ public: ASTPointer const& _name, Visibility _visibility, StateMutability _stateMutability, + bool _free, Token _kind, bool _isVirtual, ASTPointer const& _overrides, @@ -787,6 +788,7 @@ public: StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), + m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), m_body(_body) @@ -804,6 +806,7 @@ public: bool isConstructor() const { return m_kind == Token::Constructor; } bool isFallback() const { return m_kind == Token::Fallback; } bool isReceive() const { return m_kind == Token::Receive; } + bool isFree() const { return m_free; } Token kind() const { return m_kind; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector> const& modifiers() const { return m_functionModifiers; } @@ -815,6 +818,7 @@ public: } bool isVisibleViaContractTypeAccess() const override { + solAssert(!isFree(), ""); return isOrdinary() && visibility() >= Visibility::Public; } bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); } @@ -850,6 +854,7 @@ public: private: StateMutability m_stateMutability; + bool m_free; Token const m_kind; std::vector> m_functionModifiers; ASTPointer m_body; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 04f661020..2aac79837 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -234,9 +236,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation { /// Referenced declaration, set during reference resolution stage. Declaration const* referencedDeclaration = nullptr; - /// Stores a reference to the current contract. - /// This is needed because types of base contracts change depending on the context. - ContractDefinition const* contractScope = nullptr; }; struct ExpressionAnnotation: ASTAnnotation @@ -285,7 +284,6 @@ struct BinaryOperationAnnotation: ExpressionAnnotation enum class FunctionCallKind { - Unset, FunctionCall, TypeConversion, StructConstructorCall @@ -293,7 +291,7 @@ enum class FunctionCallKind struct FunctionCallAnnotation: ExpressionAnnotation { - FunctionCallKind kind = FunctionCallKind::Unset; + util::SetOnce kind; /// If true, this is the external call of a try statement. bool tryCall = false; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 5403b05c5..6d6d40775 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -203,13 +203,13 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("kind", TokenTraits::toString(_node.kind())), + make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(visibility)), make_pair("virtual", _node.markedVirtual()), @@ -467,7 +467,6 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) setJsonNode(_node, "UserDefinedTypeName", { make_pair("name", namePathToString(_node.namePath())), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), - make_pair("contractScope", idOrNull(_node.annotation().contractScope)), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; @@ -726,13 +725,16 @@ bool ASTJsonConverter::visit(FunctionCall const& _node) make_pair("arguments", toJson(_node.arguments())), make_pair("tryCall", _node.annotation().tryCall) }; + + FunctionCallKind nodeKind = *_node.annotation().kind; + if (m_legacy) { - attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall); - attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion); + attributes.emplace_back("isStructConstructorCall", nodeKind == FunctionCallKind::StructConstructorCall); + attributes.emplace_back("type_conversion", nodeKind == FunctionCallKind::TypeConversion); } else - attributes.emplace_back("kind", functionCallKind(_node.annotation().kind)); + attributes.emplace_back("kind", functionCallKind(nodeKind)); appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "FunctionCall", std::move(attributes)); return false; diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index bb5d9651c..7b16099d9 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -58,7 +58,7 @@ public: ); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream, ASTNode const& _node); - Json::Value&& toJson(ASTNode const& _node); + Json::Value toJson(ASTNode const& _node); template Json::Value toJson(std::vector> const& _nodes) { diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 2d1fda322..5e44b8616 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -381,6 +381,7 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!"); Token kind; + bool freeFunction = false; string kindStr = member(_node, "kind").asString(); if (kindStr == "constructor") @@ -391,17 +392,27 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V kind = Token::Fallback; else if (kindStr == "receive") kind = Token::Receive; + else if (kindStr == "freeFunction") + { + kind = Token::Function; + freeFunction = true; + } else astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]"); std::vector> modifiers; for (auto& mod: member(_node, "modifiers")) modifiers.push_back(createModifierInvocation(mod)); + + Visibility vis = Visibility::Default; + if (!freeFunction) + vis = visibility(_node); return createASTNode( _node, memberAsASTString(_node, "name"), - kind == Token::Constructor ? Visibility::Default : visibility(_node), + vis, stateMutability(_node), + freeFunction, kind, memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a87ae4c3f..52b206737 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -712,6 +712,8 @@ TypeResult IntegerType::binaryOperatorResult(Token _operator, Type const* _other return TypeResult::err("Exponent is fractional."); if (!rationalNumberType->integerType()) return TypeResult::err("Exponent too large."); + if (rationalNumberType->isNegative()) + return TypeResult::err("Exponentiation power is not allowed to be a negative integer literal."); } return this; } @@ -2461,52 +2463,56 @@ TypeResult StructType::interfaceType(bool _inLibrary) const TypeResult result{TypePointer{}}; + if (recursive() && !(_inLibrary && location() == DataLocation::Storage)) + return TypeResult::err( + "Recursive structs can only be passed as storage pointers to libraries, " + "not as memory objects to contract functions." + ); + util::BreadthFirstSearch breadthFirstSearch{{&m_struct}}; breadthFirstSearch.run( - [&](StructDefinition const* _struct, auto&& _addChild) { - // Check that all members have interface types. - // Return an error if at least one struct member does not have a type. - // This might happen, for example, if the type of the member does not exist. - for (ASTPointer const& variable: _struct->members()) + [&](StructDefinition const* _struct, auto&& _addChild) + { + // Check that all members have interface types. + // Return an error if at least one struct member does not have a type. + // This might happen, for example, if the type of the member does not exist. + for (ASTPointer const& variable: _struct->members()) + { + // If the struct member does not have a type return false. + // A TypeError is expected in this case. + if (!variable->annotation().type) { - // If the struct member does not have a type return false. - // A TypeError is expected in this case. - if (!variable->annotation().type) + result = TypeResult::err("Invalid type!"); + breadthFirstSearch.abort(); + return; + } + + Type const* memberType = variable->annotation().type; + + while ( + memberType->category() == Type::Category::Array || + memberType->category() == Type::Category::Mapping + ) + { + if (auto arrayType = dynamic_cast(memberType)) + memberType = arrayType->finalBaseType(false); + else if (auto mappingType = dynamic_cast(memberType)) + memberType = mappingType->valueType(); + } + + if (StructType const* innerStruct = dynamic_cast(memberType)) + _addChild(&innerStruct->structDefinition()); + else + { + auto iType = memberType->interfaceType(_inLibrary); + if (!iType.get()) { - result = TypeResult::err("Invalid type!"); + solAssert(!iType.message().empty(), "Expected detailed error message!"); + result = iType; breadthFirstSearch.abort(); return; } - - Type const* memberType = variable->annotation().type; - - while (dynamic_cast(memberType)) - memberType = dynamic_cast(memberType)->baseType(); - - if (StructType const* innerStruct = dynamic_cast(memberType)) - { - if (innerStruct->recursive() && !(_inLibrary && location() == DataLocation::Storage)) - { - result = TypeResult::err( - "Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions." - ); - breadthFirstSearch.abort(); - return; - } - else - _addChild(&innerStruct->structDefinition()); - } - else - { - auto iType = memberType->interfaceType(_inLibrary); - if (!iType.get()) - { - solAssert(!iType.message().empty(), "Expected detailed error message!"); - result = iType; - breadthFirstSearch.abort(); - return; - } - } + } } } ); @@ -3151,10 +3157,8 @@ string FunctionType::toString(bool _short) const { auto const* functionDefinition = dynamic_cast(m_declaration); solAssert(functionDefinition, ""); - auto const* contract = dynamic_cast(functionDefinition->scope()); - solAssert(contract, ""); - name += contract->annotation().canonicalName; - name += '.'; + if (auto const* contract = dynamic_cast(functionDefinition->scope())) + name += contract->annotation().canonicalName + "."; name += functionDefinition->name(); } name += '('; @@ -3275,7 +3279,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const { // Note that m_declaration might also be a state variable! solAssert(m_declaration, "Declaration needed to determine interface function type."); - bool isLibraryFunction = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool isLibraryFunction = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + isLibraryFunction = contract->isLibrary(); util::Result paramTypes = transformParametersToExternal(m_parameterTypes, isLibraryFunction); @@ -3569,7 +3576,10 @@ string FunctionType::externalSignature() const } // "inLibrary" is only relevant if this is not an event. - bool const inLibrary = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool inLibrary = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + inLibrary = contract->isLibrary(); auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary); @@ -4073,6 +4083,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const else return MemberList::MemberMap({ {"interfaceId", TypeProvider::fixedBytes(4)}, + {"name", TypeProvider::stringMemory()}, }); } else if (m_typeArgument->category() == Type::Category::Integer) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 225628a6c..cbad912a8 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -404,7 +404,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have fallback functions"); FunctionDefinition const* etherReceiver = _contract.receiveFunction(); - solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have ether receiver functions"); + solAssert(!_contract.isLibrary() || !etherReceiver, "Libraries can't have ether receiver functions"); bool needToAddCallvalueCheck = true; if (!hasPayableFunctions(_contract) && !interfaceFunctions.empty() && !_contract.isLibrary()) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b43e0cf9f..d4f70691f 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -492,8 +492,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { + auto functionCallKind = *_functionCall.annotation().kind; + CompilerContext::LocationSetter locationSetter(m_context, _functionCall); - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); @@ -517,7 +519,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } FunctionTypePointer functionType; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -548,7 +550,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(found, ""); } - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -1734,9 +1736,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) Type::Category category = _memberAccess.annotation().type->category(); solAssert( category == Type::Category::TypeType || - category == Type::Category::Module, + category == Type::Category::Module || + category == Type::Category::Function, "" ); + if (auto funType = dynamic_cast(_memberAccess.annotation().type)) + { + auto const* funDef = dynamic_cast(_memberAccess.annotation().referencedDeclaration); + solAssert(funDef && funDef->isFree(), ""); + solAssert(funType->kind() == FunctionType::Kind::Internal, ""); + utils().pushCombinedFunctionEntryLabel(*funDef); + } break; } default: diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 69e8a7131..542a67613 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -597,6 +597,139 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) }); } +string YulUtilFunctions::overflowCheckedIntExpFunction( + IntegerType const& _type, + IntegerType const& _exponentType +) +{ + solAssert(!_exponentType.isSigned(), ""); + + string functionName = "checked_exp_" + _type.identifier() + "_" + _exponentType.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent) -> power { + base := (base) + exponent := (exponent) + + power := (base, exponent, , ) + + power := (base, exponent, ) + + + } + )") + ("functionName", functionName) + ("signed", _type.isSigned()) + ("exp", _type.isSigned() ? overflowCheckedSignedExpFunction() : overflowCheckedUnsignedExpFunction()) + ("maxValue", toCompactHexWithPrefix(_type.max())) + ("minValue", toCompactHexWithPrefix(_type.min())) + ("baseCleanupFunction", cleanupFunction(_type)) + ("exponentCleanupFunction", cleanupFunction(_exponentType)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedUnsignedExpFunction() +{ + string functionName = "checked_exp_unsigned"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent, max) -> power { + // This function currently cannot be inlined because of the + // "leave" statements. We have to improve the optimizer. + + // Note that 0**0 == 1 + if iszero(exponent) { power := 1 leave } + if iszero(base) { power := 0 leave } + + power := 1 + + for { } gt(exponent, 1) {} + { + // overflow check for base * base + if gt(base, div(max, base)) { revert(0, 0) } + if and(exponent, 1) + { + // no check needed here because base >= power + power := mul(power, base) + } + base := mul(base, base) + exponent := (exponent) + } + if gt(power, div(max, base)) { revert(0, 0) } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedSignedExpFunction() +{ + string functionName = "checked_exp_signed"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent, min, max) -> power { + // Currently, `leave` avoids this function being inlined. + // We have to improve the optimizer. + + // Note that 0**0 == 1 + switch exponent + case 0 { power := 1 leave } + case 1 { power := base leave } + if iszero(base) { power := 0 leave } + + power := 1 + + // We pull out the first iteration because it is the only one in which + // base can be negative. + // Exponent is at least 2 here. + + // overflow check for base * base + switch sgt(base, 0) + case 1 { if gt(base, div(max, base)) { revert(0, 0) } } + case 0 { if slt(base, sdiv(max, base)) { revert(0, 0) } } + if and(exponent, 1) + { + power := base + } + base := mul(base, base) + exponent := (exponent) + + // Below this point, base is always positive. + + for { } gt(exponent, 1) {} + { + // overflow check for base * base + if gt(base, div(max, base)) { revert(0, 0) } + if and(exponent, 1) + { + // No checks for power := mul(power, base) needed, because the check + // for base * base above is sufficient, since: + // |power| <= base (proof by induction) and thus: + // |power * base| <= base * base <= max <= |min| + power := mul(power, base) + } + base := mul(base, base) + exponent := (exponent) + } + + if and(sgt(power, 0), gt(power, div(max, base))) { revert(0, 0) } + if and(slt(power, 0), slt(power, sdiv(min, base))) { revert(0, 0) } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + string YulUtilFunctions::extractByteArrayLengthFunction() { string functionName = "extract_byte_array_length"; @@ -1911,8 +2044,23 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) break; } case Type::Category::Struct: - solUnimplementedAssert(false, "Struct conversion not implemented."); + { + solAssert(toCategory == Type::Category::Struct, ""); + auto const& fromStructType = dynamic_cast(_from); + auto const& toStructType = dynamic_cast(_to); + solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); + + solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), ""); + solUnimplementedAssert(toStructType.location() == DataLocation::Memory, ""); + solUnimplementedAssert(fromStructType.location() == DataLocation::CallData, ""); + + body = Whiskers(R"( + converted := (value, calldatasize()) + )")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder( + {&toStructType} + )).render(); break; + } case Type::Category::FixedBytes: { FixedBytesType const& from = dynamic_cast(_from); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 19ca72dd0..7af910a6d 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -125,6 +125,21 @@ public: /// signature: (x, y) -> diff std::string overflowCheckedIntSubFunction(IntegerType const& _type); + /// @returns the name of the exponentiation function. + /// signature: (base, exponent) -> power + std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType); + + /// Generic unsigned checked exponentiation function. + /// Reverts if the result is larger than max. + /// signature: (base, exponent, max) -> power + std::string overflowCheckedUnsignedExpFunction(); + + /// Generic signed checked exponentiation function. + /// Reverts if the result is smaller than min or larger than max. + /// The code relies on max <= |min| and min < 0. + /// signature: (base, exponent, min, max) -> power + std::string overflowCheckedSignedExpFunction(); + /// @returns the name of a function that fetches the length of the given /// array /// signature: (array) -> length diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 469beea2f..a833fdfa5 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -98,8 +98,9 @@ public: void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset); bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); } - std::pair storageLocationOfVariable(VariableDeclaration const& _varDecl) const + std::pair storageLocationOfStateVariable(VariableDeclaration const& _varDecl) const { + solAssert(isStateVariable(_varDecl), ""); return m_stateVariables.at(&_varDecl); } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index f7f66b4f5..8fb932d01 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -305,7 +305,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) string code; - auto const& location = m_context.storageLocationOfVariable(_varDecl); + auto const& location = m_context.storageLocationOfStateVariable(_varDecl); code += Whiskers(R"( let slot := let offset := diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index a7ef82784..3ab3c8888 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -75,18 +75,30 @@ struct CopyTranslate: public yul::ASTCopier { solAssert(reference.isOffset != reference.isSlot, ""); - pair slot_offset = m_context.storageLocationOfVariable(*varDecl); + string value; + if (varDecl->isStateVariable()) + value = + reference.isSlot ? + m_context.storageLocationOfStateVariable(*varDecl).first.str() : + to_string(m_context.storageLocationOfStateVariable(*varDecl).second); + else + { + solAssert(varDecl->isLocalVariable(), ""); + if (reference.isSlot) + value = IRVariable{*varDecl}.part("slot").name(); + else if (varDecl->type()->isValueType()) + value = IRVariable{*varDecl}.part("offset").name(); + else + { + solAssert(!IRVariable{*varDecl}.hasPart("offset"), ""); + value = "0"; + } + } - string const value = reference.isSlot ? - slot_offset.first.str() : - to_string(slot_offset.second); - - return yul::Literal{ - _identifier.location, - yul::LiteralKind::Number, - yul::YulString{value}, - {} - }; + if (isdigit(value.front())) + return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}}; + else + return yul::Identifier{_identifier.location, yul::YulString{value}}; } } return ASTCopier::operator()(_identifier); @@ -152,8 +164,8 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va _varDecl.immutable() ? IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ - util::toCompactHexWithPrefix(m_context.storageLocationOfVariable(_varDecl).first), - m_context.storageLocationOfVariable(_varDecl).second + util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), + m_context.storageLocationOfStateVariable(_varDecl).second }}, *_varDecl.value() ); @@ -277,6 +289,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types."); IRVariable leftIntermediate = readFromLValue(*m_currentLValue); + solAssert(binaryOperator != Token::Exp, ""); if (TokenTraits::isShiftOp(binaryOperator)) { solAssert(type(_assignment) == leftIntermediate.type(), ""); @@ -593,11 +606,17 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) solAssert(false, "Unknown comparison operator."); define(_binOp) << expr << "\n"; } - else if (TokenTraits::isShiftOp(op)) + else if (TokenTraits::isShiftOp(op) || op == Token::Exp) { IRVariable left = convert(_binOp.leftExpression(), *commonType); IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType()); - define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n"; + if (op == Token::Exp) + define(_binOp) << m_utils.overflowCheckedIntExpFunction( + dynamic_cast(left.type()), + dynamic_cast(right.type()) + ) << "(" << left.name() << ", " << right.name() << ")\n"; + else + define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n"; } else { @@ -624,12 +643,9 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { - solUnimplementedAssert( - _functionCall.annotation().kind != FunctionCallKind::Unset, - "This type of function call is not yet implemented" - ); + auto functionCallKind = *_functionCall.annotation().kind; - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { solAssert( _functionCall.expression().annotation().type->category() == Type::Category::TypeType, @@ -641,7 +657,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } FunctionTypePointer functionType = nullptr; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -672,7 +688,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) arguments.push_back(callArguments[static_cast(std::distance(callArgumentNames.begin(), it))]); } - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -2022,8 +2038,8 @@ void IRGeneratorForStatements::handleVariableReference( setLValue(_referencingExpression, IRLValue{ *_variable.annotation().type, IRLValue::Storage{ - toCompactHexWithPrefix(m_context.storageLocationOfVariable(_variable).first), - m_context.storageLocationOfVariable(_variable).second + toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first), + m_context.storageLocationOfStateVariable(_variable).second } }); else diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp index 3fce5288a..92ec87da6 100644 --- a/libsolidity/codegen/ir/IRVariable.cpp +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -53,6 +53,17 @@ IRVariable IRVariable::part(string const& _name) const solAssert(false, "Invalid stack item name: " + _name); } +bool IRVariable::hasPart(std::string const& _name) const +{ + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemName == _name) + { + solAssert(itemName.empty() || itemType, ""); + return true; + } + return false; +} + vector IRVariable::stackSlots() const { vector result; diff --git a/libsolidity/codegen/ir/IRVariable.h b/libsolidity/codegen/ir/IRVariable.h index 71413cc38..1f403a07d 100644 --- a/libsolidity/codegen/ir/IRVariable.h +++ b/libsolidity/codegen/ir/IRVariable.h @@ -71,6 +71,10 @@ public: /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. IRVariable part(std::string const& _slot) const; + /// @returns true if variable contains @a _name component + /// @a _name name of the component that is being checked + bool hasPart(std::string const& _name) const; + /// @returns a vector containing the names of the stack slots of the variable. std::vector stackSlots() const; diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 69165841b..503f48da8 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -198,6 +198,24 @@ bool BMC::visit(IfStatement const& _node) return false; } +bool BMC::visit(Conditional const& _op) +{ + m_context.pushSolver(); + _op.condition().accept(*this); + + if (isRootFunction()) + addVerificationTarget( + VerificationTarget::Type::ConstantCondition, + expr(_op.condition()), + &_op.condition() + ); + m_context.popSolver(); + + SMTEncoder::visit(_op); + + return false; +} + // Here we consider the execution of two branches: // Branch 1 assumes the loop condition to be true and executes the loop once, // after resetting touched variables. @@ -337,8 +355,9 @@ void BMC::endVisit(UnaryOperation const& _op) void BMC::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + auto functionCallKind = *_funCall.annotation().kind; + + if (functionCallKind != FunctionCallKind::FunctionCall) { SMTEncoder::endVisit(_funCall); return; @@ -498,8 +517,37 @@ pair BMC::arithmeticOperation( auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); + auto const* intType = dynamic_cast(_commonType); + if (!intType) + intType = TypeProvider::uint256(); + + // Mod does not need underflow/overflow checks. + if (_op == Token::Mod) + return values; + + VerificationTarget::Type type; + // The order matters here: + // If _op is Div and intType is signed, we only care about overflow. + if (_op == Token::Div) + { + if (intType->isSigned()) + // Signed division can only overflow. + type = VerificationTarget::Type::Overflow; + else + // Unsigned division cannot underflow/overflow. + return values; + } + else if (intType->isSigned()) + type = VerificationTarget::Type::UnderOverflow; + else if (_op == Token::Sub) + type = VerificationTarget::Type::Underflow; + else if (_op == Token::Add || _op == Token::Mul) + type = VerificationTarget::Type::Overflow; + else + solAssert(false, ""); + addVerificationTarget( - VerificationTarget::Type::UnderOverflow, + type, values.second, &_expression ); @@ -605,12 +653,19 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con _target.type == VerificationTarget::Type::UnderOverflow, "" ); - IntegerType const* intType = nullptr; - if (auto const* type = dynamic_cast(_target.expression->annotation().type)) - intType = type; - else + + if ( + m_solvedTargets.count(_target.expression) && ( + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Underflow) || + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow) + ) + ) + return; + + auto const* intType = dynamic_cast(_target.expression->annotation().type); + if (!intType) intType = TypeProvider::uint256(); - solAssert(intType, ""); + checkCondition( _target.constraints && _constraints && _target.value < smt::minValue(*intType), _target.callStack, @@ -631,13 +686,19 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons _target.type == VerificationTarget::Type::UnderOverflow, "" ); - IntegerType const* intType = nullptr; - if (auto const* type = dynamic_cast(_target.expression->annotation().type)) - intType = type; - else + + if ( + m_solvedTargets.count(_target.expression) && ( + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Overflow) || + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow) + ) + ) + return; + + auto const* intType = dynamic_cast(_target.expression->annotation().type); + if (!intType) intType = TypeProvider::uint256(); - solAssert(intType, ""); checkCondition( _target.constraints && _constraints && _target.value > smt::maxValue(*intType), _target.callStack, diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 324595bba..bd86d7360 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -84,6 +84,7 @@ private: bool visit(FunctionDefinition const& _node) override; void endVisit(FunctionDefinition const& _node) override; bool visit(IfStatement const& _node) override; + bool visit(Conditional const& _node) override; bool visit(WhileStatement const& _node) override; bool visit(ForStatement const& _node) override; void endVisit(UnaryOperation const& _node) override; diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 304754529..65df7e203 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -147,7 +147,7 @@ void CHC::endVisit(ContractDefinition const& _contract) else inlineConstructorHierarchy(_contract); - connectBlocks(m_currentBlock, summary(_contract), m_error.currentValue() == 0); + connectBlocks(m_currentBlock, summary(_contract)); clearIndices(m_currentContract, nullptr); vector symbArgs = currentFunctionVariables(*m_currentContract); @@ -424,9 +424,9 @@ bool CHC::visit(ForStatement const& _for) void CHC::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + auto functionCallKind = *_funCall.annotation().kind; - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (functionCallKind != FunctionCallKind::FunctionCall) { SMTEncoder::endVisit(_funCall); return; @@ -452,6 +452,9 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::Creation: + SMTEncoder::endVisit(_funCall); + unknownFunctionCall(_funCall); + break; case FunctionType::Kind::KECCAK256: case FunctionType::Kind::ECRecover: case FunctionType::Kind::SHA256: @@ -459,9 +462,7 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BlockHash: case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: - SMTEncoder::endVisit(_funCall); - unknownFunctionCall(_funCall); - break; + [[fallthrough]]; default: SMTEncoder::endVisit(_funCall); break; @@ -601,14 +602,74 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) auto previousError = m_error.currentValue(); m_error.increaseIndex(); - addArrayPopVerificationTarget(&_arrayPop, m_error.currentValue()); - connectBlocks( - m_currentBlock, - m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction), - currentPathConditions() && symbArray->length() <= 0 && m_error.currentValue() == newErrorId(_arrayPop) + addVerificationTarget(&_arrayPop, VerificationTarget::Type::PopEmptyArray, m_error.currentValue()); + + smtutil::Expression target = (symbArray->length() <= 0) && (m_error.currentValue() == newErrorId(_arrayPop)); + m_context.addAssertion((m_error.currentValue() == previousError) || target); +} + +pair CHC::arithmeticOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType, + frontend::Expression const& _expression +) +{ + auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); + + IntegerType const* intType = nullptr; + if (auto const* type = dynamic_cast(_commonType)) + intType = type; + else + intType = TypeProvider::uint256(); + + // Mod does not need underflow/overflow checks. + // Div only needs overflow check for signed types. + if (_op == Token::Mod || (_op == Token::Div && !intType->isSigned())) + return values; + + auto previousError = m_error.currentValue(); + m_error.increaseIndex(); + + VerificationTarget::Type targetType; + unsigned errorId = newErrorId(_expression); + + optional target; + if (_op == Token::Div) + { + targetType = VerificationTarget::Type::Overflow; + target = values.second > intType->maxValue() && m_error.currentValue() == errorId; + } + else if (intType->isSigned()) + { + unsigned secondErrorId = newErrorId(_expression); + targetType = VerificationTarget::Type::UnderOverflow; + target = (values.second < intType->minValue() && m_error.currentValue() == errorId) || + (values.second > intType->maxValue() && m_error.currentValue() == secondErrorId); + } + else if (_op == Token::Sub) + { + targetType = VerificationTarget::Type::Underflow; + target = values.second < intType->minValue() && m_error.currentValue() == errorId; + } + else if (_op == Token::Add || _op == Token::Mul) + { + targetType = VerificationTarget::Type::Overflow; + target = values.second > intType->maxValue() && m_error.currentValue() == errorId; + } + else + solAssert(false, ""); + + addVerificationTarget( + &_expression, + targetType, + m_error.currentValue() ); - m_context.addAssertion(m_error.currentValue() == previousError); + m_context.addAssertion((m_error.currentValue() == previousError) || *target); + + return values; } void CHC::resetSourceAnalysis() @@ -1174,26 +1235,25 @@ void CHC::addVerificationTarget( m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId}); } -void CHC::addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId) -{ - addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId); -} - -void CHC::addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId) +void CHC::addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId) { solAssert(m_currentContract, ""); - solAssert(m_currentFunction, ""); - if (m_currentFunction->isConstructor()) - addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, summary(*m_currentContract), smtutil::Expression(true), _errorId); + if (!m_currentFunction || m_currentFunction->isConstructor()) + addVerificationTarget(_scope, _type, summary(*m_currentContract), smtutil::Expression(true), _errorId); else { auto iface = (*m_interfaces.at(m_currentContract))(initialStateVariables()); auto sum = summary(*m_currentFunction); - addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, iface, sum, _errorId); + addVerificationTarget(_scope, _type, iface, sum, _errorId); } } +void CHC::addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId) +{ + addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId); +} + void CHC::checkVerificationTargets() { for (auto const& [scope, target]: m_verificationTargets) @@ -1203,8 +1263,12 @@ void CHC::checkVerificationTargets() else { string satMsg; + string satMsgUnderflow; + string satMsgOverflow; string unknownMsg; ErrorId errorReporterId; + ErrorId underflowErrorId = 3944_error; + ErrorId overflowErrorId = 4984_error; if (target.type == VerificationTarget::Type::PopEmptyArray) { @@ -1213,12 +1277,51 @@ void CHC::checkVerificationTargets() unknownMsg = "Empty array \"pop\" might happen here."; errorReporterId = 2529_error; } + else if ( + target.type == VerificationTarget::Type::Underflow || + target.type == VerificationTarget::Type::Overflow || + target.type == VerificationTarget::Type::UnderOverflow + ) + { + auto const* expr = dynamic_cast(scope); + solAssert(expr, ""); + auto const* intType = dynamic_cast(expr->annotation().type); + if (!intType) + intType = TypeProvider::uint256(); + + satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ") happens here"; + satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ") happens here"; + if (target.type == VerificationTarget::Type::Underflow) + { + satMsg = satMsgUnderflow; + errorReporterId = underflowErrorId; + } + else if (target.type == VerificationTarget::Type::Overflow) + { + satMsg = satMsgOverflow; + errorReporterId = overflowErrorId; + } + } else solAssert(false, ""); auto it = m_errorIds.find(scope->id()); solAssert(it != m_errorIds.end(), ""); - checkAndReportTarget(scope, target, it->second, errorReporterId, satMsg, unknownMsg); + unsigned errorId = it->second; + + if (target.type != VerificationTarget::Type::UnderOverflow) + checkAndReportTarget(scope, target, errorId, errorReporterId, satMsg, unknownMsg); + else + { + auto specificTarget = target; + specificTarget.type = VerificationTarget::Type::Underflow; + checkAndReportTarget(scope, specificTarget, errorId, underflowErrorId, satMsgUnderflow, unknownMsg); + + ++it; + solAssert(it != m_errorIds.end(), ""); + specificTarget.type = VerificationTarget::Type::Overflow; + checkAndReportTarget(scope, specificTarget, it->second, overflowErrorId, satMsgOverflow, unknownMsg); + } } } } diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8342524bb..ac1315317 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -84,6 +84,14 @@ private: void externalFunctionCall(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override; + /// Creates underflow/overflow verification targets. + std::pair arithmeticOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType, + Expression const& _expression + ) override; //@} struct IdCompare @@ -197,8 +205,8 @@ private: std::pair query(smtutil::Expression const& _query, langutil::SourceLocation const& _location); void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); + void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _errorId); void addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); - void addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId); void checkVerificationTargets(); // Forward declaration. Definition is below. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 50ede6d13..8cbed6712 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -420,8 +420,11 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) _tuple.location(), "Assertion checker does not yet implement inline arrays." ); - else if (_tuple.annotation().type->category() == Type::Category::Tuple) + else if (_tuple.components().size() == 1) + defineExpr(_tuple, expr(*_tuple.components().front())); + else { + solAssert(_tuple.annotation().type->category() == Type::Category::Tuple, ""); auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_tuple)); solAssert(symbTuple, ""); auto const& symbComponents = symbTuple->components(); @@ -445,28 +448,25 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) } } } - else - { - /// Parenthesized expressions are also TupleExpression regardless their type. - auto const& components = _tuple.components(); - solAssert(components.size() == 1, ""); - defineExpr(_tuple, expr(*components.front())); - } } void SMTEncoder::endVisit(UnaryOperation const& _op) { + if (TokenTraits::isBitOp(_op.getOperator())) + return bitwiseNotOperation(_op); if (_op.annotation().type->category() == Type::Category::RationalNumber) return; createExpr(_op); + auto const* subExpr = innermostTuple(_op.subExpression()); + switch (_op.getOperator()) { case Token::Not: // ! { solAssert(smt::isBool(_op.annotation().type->category()), ""); - defineExpr(_op, !expr(_op.subExpression())); + defineExpr(_op, !expr(*subExpr)); break; } case Token::Inc: // ++ (pre- or postfix) @@ -474,8 +474,8 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) { auto cat = _op.annotation().type->category(); solAssert(smt::isInteger(cat) || smt::isFixedPoint(cat), ""); - solAssert(_op.subExpression().annotation().willBeWrittenTo, ""); - if (auto identifier = dynamic_cast(&_op.subExpression())) + solAssert(subExpr->annotation().willBeWrittenTo, ""); + if (auto identifier = dynamic_cast(subExpr)) { auto decl = identifierToVariable(*identifier); solAssert(decl, ""); @@ -484,12 +484,12 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); assignment(*decl, newValue); } - else if (dynamic_cast(&_op.subExpression())) + else if (dynamic_cast(subExpr)) { - auto innerValue = expr(_op.subExpression()); + auto innerValue = expr(*subExpr); auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); - arrayIndexAssignment(_op.subExpression(), newValue); + arrayIndexAssignment(*subExpr, newValue); } else m_errorReporter.warning( @@ -502,25 +502,24 @@ void SMTEncoder::endVisit(UnaryOperation const& _op) } case Token::Sub: // - { - defineExpr(_op, 0 - expr(_op.subExpression())); + defineExpr(_op, 0 - expr(*subExpr)); break; } case Token::Delete: { - auto const& subExpr = _op.subExpression(); - if (auto decl = identifierToVariable(subExpr)) + if (auto decl = identifierToVariable(*subExpr)) { m_context.newValue(*decl); m_context.setZeroValue(*decl); } else { - solAssert(m_context.knownExpression(subExpr), ""); - auto const& symbVar = m_context.expression(subExpr); + solAssert(m_context.knownExpression(*subExpr), ""); + auto const& symbVar = m_context.expression(*subExpr); symbVar->increaseIndex(); m_context.setZeroValue(*symbVar); - if (dynamic_cast(&_op.subExpression())) - arrayIndexAssignment(_op.subExpression(), symbVar->currentValue()); + if (dynamic_cast(subExpr)) + arrayIndexAssignment(*subExpr, symbVar->currentValue()); else m_errorReporter.warning( 2683_error, @@ -579,11 +578,33 @@ void SMTEncoder::endVisit(BinaryOperation const& _op) ); } +bool SMTEncoder::visit(Conditional const& _op) +{ + _op.condition().accept(*this); + + auto indicesEndTrue = visitBranch(&_op.trueExpression(), expr(_op.condition())); + auto touchedVars = touchedVariables(_op.trueExpression()); + + auto indicesEndFalse = visitBranch(&_op.falseExpression(), !expr(_op.condition())); + touchedVars += touchedVariables(_op.falseExpression()); + + mergeVariables(touchedVars, expr(_op.condition()), indicesEndTrue, indicesEndFalse); + + defineExpr(_op, smtutil::Expression::ite( + expr(_op.condition()), + expr(_op.trueExpression()), + expr(_op.falseExpression()) + )); + + return false; +} + void SMTEncoder::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + auto functionCallKind = *_funCall.annotation().kind; + createExpr(_funCall); - if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { m_errorReporter.warning( 4639_error, @@ -593,7 +614,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) return; } - if (_funCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { visitTypeConversion(_funCall); return; @@ -753,7 +774,7 @@ void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(*_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); solAssert(_funCall.arguments().size() == 1, ""); auto argument = _funCall.arguments().front(); unsigned argSize = argument->annotation().type->storageBytes(); @@ -1083,25 +1104,22 @@ void SMTEncoder::arrayPop(FunctionCall const& _funCall) auto oldElements = symbArray->elements(); auto oldLength = symbArray->length(); - m_context.addAssertion(oldLength > 0); symbArray->increaseIndex(); m_context.addAssertion(symbArray->elements() == oldElements); auto newLength = smtutil::Expression::ite( - oldLength == 0, - smt::maxValue(*TypeProvider::uint256()), - oldLength - 1 + oldLength > 0, + oldLength - 1, + 0 ); - m_context.addAssertion(symbArray->length() == oldLength - 1); + m_context.addAssertion(symbArray->length() == newLength); arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue()); } void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression const& _array) { - Expression const* expr = &_expr; - if (auto const* tupleExpr = dynamic_cast(expr)) - expr = innermostTuple(*tupleExpr); + Expression const* expr = innermostTuple(_expr); if (auto const* id = dynamic_cast(expr)) { @@ -1385,20 +1403,7 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) auto commonType = _op.annotation().commonType; solAssert(commonType, ""); - unsigned bvSize = 256; - bool isSigned = false; - if (auto const* intType = dynamic_cast(commonType)) - { - bvSize = intType->numBits(); - isSigned = intType->isSigned(); - } - else if (auto const* fixedType = dynamic_cast(commonType)) - { - bvSize = fixedType->numBits(); - isSigned = fixedType->isSigned(); - } - else if (auto const* fixedBytesType = dynamic_cast(commonType)) - bvSize = fixedBytesType->numBytes() * 8; + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(commonType); auto bvLeft = smtutil::Expression::int2bv(expr(_op.leftExpression(), commonType), bvSize); auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize); @@ -1406,18 +1411,26 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) optional result; if (_op.getOperator() == Token::BitAnd) result = bvLeft & bvRight; - // TODO implement the other operators - else - m_errorReporter.warning( - 1093_error, - _op.location(), - "Assertion checker does not yet implement this bitwise operator." - ); + else if (_op.getOperator() == Token::BitOr) + result = bvLeft | bvRight; + else if (_op.getOperator() == Token::BitXor) + result = bvLeft ^ bvRight; + solAssert(result, ""); if (result) defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned)); } +void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op) +{ + solAssert(_op.getOperator() == Token::BitNot, ""); + + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_op.annotation().type); + + auto bvOperand = smtutil::Expression::int2bv(expr(_op.subExpression(), _op.annotation().type), bvSize); + defineExpr(_op, smtutil::Expression::bv2int(~bvOperand, isSigned)); +} + smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Expression _right, IntegerType const& _type) { // Signed division in SMTLIB2 rounds differently for negative division. @@ -1443,9 +1456,7 @@ void SMTEncoder::assignment( "Tuple assignments should be handled by tupleAssignment." ); - Expression const* left = &_left; - if (auto const* tuple = dynamic_cast(left)) - left = innermostTuple(*tuple); + Expression const* left = innermostTuple(_left); if (!smt::isSupportedType(_type->category())) { @@ -1473,15 +1484,16 @@ void SMTEncoder::assignment( void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right) { - auto lTuple = dynamic_cast(innermostTuple(dynamic_cast(_left))); + auto lTuple = dynamic_cast(innermostTuple(_left)); solAssert(lTuple, ""); + Expression const* right = innermostTuple(_right); auto const& lComponents = lTuple->components(); // If both sides are tuple expressions, we individually and potentially // recursively assign each pair of components. // This is because of potential type conversion. - if (auto rTuple = dynamic_cast(&_right)) + if (auto rTuple = dynamic_cast(right)) { auto const& rComponents = rTuple->components(); solAssert(lComponents.size() == rComponents.size(), ""); @@ -1502,13 +1514,13 @@ void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _rig } else { - auto rType = dynamic_cast(_right.annotation().type); + auto rType = dynamic_cast(right->annotation().type); solAssert(rType, ""); auto const& rComponents = rType->components(); solAssert(lComponents.size() == rComponents.size(), ""); - auto symbRight = expr(_right); + auto symbRight = expr(*right); solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, ""); for (unsigned i = 0; i < lComponents.size(); ++i) @@ -1899,10 +1911,12 @@ Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) return base; } -Expression const* SMTEncoder::innermostTuple(TupleExpression const& _tuple) +Expression const* SMTEncoder::innermostTuple(Expression const& _expr) { - solAssert(!_tuple.isInlineArray(), ""); - TupleExpression const* tuple = &_tuple; + auto const* tuple = dynamic_cast(&_expr); + if (!tuple || tuple->isInlineArray()) + return &_expr; + Expression const* expr = tuple; while (tuple && !tuple->isInlineArray() && tuple->components().size() == 1) { @@ -1948,7 +1962,7 @@ string SMTEncoder::extraComment() FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall const& _funCall) { - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall) return nullptr; FunctionDefinition const* funDef = nullptr; diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index ec097a2c9..fb5dce405 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -54,8 +54,9 @@ public: /// @returns the leftmost identifier in a multi-d IndexAccess. static Expression const* leftmostBase(IndexAccess const& _indexAccess); - /// @returns the innermost element in a chain of 1-tuples. - static Expression const* innermostTuple(TupleExpression const& _tuple); + /// @returns the innermost element in a chain of 1-tuples if applicable, + /// otherwise _expr. + static Expression const* innermostTuple(Expression const& _expr); /// @returns the FunctionDefinition of a FunctionCall /// if possible or nullptr. @@ -83,6 +84,7 @@ protected: void endVisit(UnaryOperation const& _node) override; bool visit(BinaryOperation const& _node) override; void endVisit(BinaryOperation const& _node) override; + bool visit(Conditional const& _node) override; void endVisit(FunctionCall const& _node) override; bool visit(ModifierInvocation const& _node) override; void endVisit(Identifier const& _node) override; @@ -114,6 +116,7 @@ protected: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); void bitwiseOperation(BinaryOperation const& _op); + void bitwiseNotOperation(UnaryOperation const& _op); void initContract(ContractDefinition const& _contract); void initFunction(FunctionDefinition const& _function); diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index b149cb69c..965130bb7 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -92,13 +92,35 @@ SortPointer smtSort(frontend::Type const& _type) string tupleName; if ( auto arrayType = dynamic_cast(&_type); - (arrayType && arrayType->isString()) || + (arrayType && (arrayType->isString() || arrayType->isByteArray())) || _type.category() == frontend::Type::Category::ArraySlice || _type.category() == frontend::Type::Category::StringLiteral ) - tupleName = "bytes_tuple"; + tupleName = "bytes"; + else if (auto arrayType = dynamic_cast(&_type)) + { + auto baseType = arrayType->baseType(); + // Solidity allows implicit conversion also when assigning arrays. + // So if the base type potentially has a size, that size cannot go + // in the tuple's name. + if (auto tupleSort = dynamic_pointer_cast(array->range)) + tupleName = tupleSort->name; + else if ( + baseType->category() == frontend::Type::Category::Integer || + baseType->category() == frontend::Type::Category::FixedPoint + ) + tupleName = "uint"; + else if (baseType->category() == frontend::Type::Category::FixedBytes) + tupleName = "fixedbytes"; + else + tupleName = arrayType->baseType()->toString(true); + + tupleName += "[]"; + } else - tupleName = _type.toString(true) + "_tuple"; + tupleName = _type.toString(true); + + tupleName += "_tuple"; return make_shared( tupleName, @@ -410,6 +432,18 @@ smtutil::Expression zeroValue(frontend::TypePointer const& _type) return 0; } +pair typeBvSizeAndSignedness(frontend::TypePointer const& _type) +{ + if (auto const* intType = dynamic_cast(_type)) + return {intType->numBits(), intType->isSigned()}; + else if (auto const* fixedType = dynamic_cast(_type)) + return {fixedType->numBits(), fixedType->isSigned()}; + else if (auto const* fixedBytesType = dynamic_cast(_type)) + return {fixedBytesType->numBytes() * 8, false}; + else + solAssert(false, ""); +} + void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context) { setSymbolicUnknownValue(_variable.currentValue(), _variable.type(), _context); diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index 401c9f0b6..8e08783d8 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -67,6 +67,8 @@ smtutil::Expression minValue(frontend::IntegerType const& _type); smtutil::Expression maxValue(frontend::IntegerType const& _type); smtutil::Expression zeroValue(frontend::TypePointer const& _type); +std::pair typeBvSizeAndSignedness(frontend::TypePointer const& type); + void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _context); void setSymbolicZeroValue(smtutil::Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context); void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cfb7de46e..77492ef2d 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -107,8 +107,11 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) case Token::Enum: nodes.push_back(parseEnumDefinition()); break; + case Token::Function: + nodes.push_back(parseFunctionDefinition(true)); + break; default: - fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition."); + fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition."); } } solAssert(m_recursionDepth == 0, ""); @@ -548,7 +551,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari return result; } -ASTPointer Parser::parseFunctionDefinition() +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -607,6 +610,7 @@ ASTPointer Parser::parseFunctionDefinition() name, header.visibility, header.stateMutability, + _freeFunction, kind, header.isVirtual, header.overrides, @@ -1046,7 +1050,7 @@ ASTPointer Parser::parseMapping() } else fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type"); - expectToken(Token::Arrow); + expectToken(Token::DoubleArrow); ASTPointer valueType = parseTypeName(); nodeFactory.markEndPosition(); expectToken(Token::RParen); @@ -1102,7 +1106,7 @@ ASTPointer Parser::parseBlock(ASTPointer const& _docString) BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ m_inParserRecovery = true; } - if (m_parserErrorRecovery) + if (m_inParserRecovery) expectTokenOrConsumeUntil(Token::RBrace, "Block"); else expectToken(Token::RBrace); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index b769545da..0047ed6e4 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -92,7 +92,7 @@ private: ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(); + ASTPointer parseFunctionDefinition(bool _freeFunction = false); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseEnumValue(); diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 3672657e8..485218999 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -22,6 +22,7 @@ set(sources LazyInit.h picosha2.h Result.h + SetOnce.h StringUtils.cpp StringUtils.h SwarmHash.cpp diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index b61ba82a2..b01e24c38 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -78,6 +78,7 @@ template std::set& operator+=(std::set& _a, U& _a.insert(std::move(x)); return _a; } + /// Concatenate two vectors of elements. template inline std::vector operator+(std::vector const& _a, std::vector const& _b) @@ -86,6 +87,7 @@ inline std::vector operator+(std::vector const& _a, std::vector const& ret += _b; return ret; } + /// Concatenate two vectors of elements, moving them. template inline std::vector operator+(std::vector&& _a, std::vector&& _b) @@ -97,6 +99,7 @@ inline std::vector operator+(std::vector&& _a, std::vector&& _b) ret += std::move(_b); return ret; } + /// Concatenate something to a sets of elements. template inline std::set operator+(std::set const& _a, U&& _b) @@ -105,6 +108,7 @@ inline std::set operator+(std::set const& _a, U&& _b) ret += std::forward(_b); return ret; } + /// Concatenate something to a sets of elements, move variant. template inline std::set operator+(std::set&& _a, U&& _b) diff --git a/libsolutil/SetOnce.h b/libsolutil/SetOnce.h new file mode 100644 index 000000000..289258e7b --- /dev/null +++ b/libsolutil/SetOnce.h @@ -0,0 +1,86 @@ +/* + 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::util +{ + +DEV_SIMPLE_EXCEPTION(BadSetOnceReassignment); +DEV_SIMPLE_EXCEPTION(BadSetOnceAccess); + +/// A class that stores a value that can only be set once +/// \tparam T the type of the stored value +template +class SetOnce +{ +public: + /// Initializes the class to have no stored value. + SetOnce() = default; + + // Not copiable + SetOnce(SetOnce const&) = delete; + SetOnce(SetOnce&&) = delete; + + // Not movable + SetOnce& operator=(SetOnce const&) = delete; + SetOnce& operator=(SetOnce&&) = delete; + + /// @brief Sets the stored value to \p _newValue + /// @throws BadSetOnceReassignment when the stored value has already been set + /// @return `*this` + constexpr SetOnce& operator=(T _newValue) & + { + assertThrow( + !m_value.has_value(), + BadSetOnceReassignment, + "Attempt to reassign to a SetOnce that already has a value." + ); + + m_value.emplace(std::move(_newValue)); + return *this; + } + + /// @return A reference to the stored value. The returned reference has the same lifetime as `*this`. + /// @throws BadSetOnceAccess when the stored value has not yet been set + T const& operator*() const + { + assertThrow( + m_value.has_value(), + BadSetOnceAccess, + "Attempt to access the value of a SetOnce that does not have a value." + ); + + return m_value.value(); + } + + /// @return A reference to the stored value. The referent of the returned pointer has the same lifetime as `*this`. + /// @throws BadSetOnceAccess when the stored value has not yet been set + T const* operator->() const { return std::addressof(**this); } + +private: + std::optional m_value = std::nullopt; +}; + +} diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 2c7aa1f93..2baefa16f 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -116,26 +116,24 @@ Statement Parser::parseStatement() { Statement stmt{createWithLocation()}; checkBreakContinuePosition("break"); - m_scanner->next(); + advance(); return stmt; } case Token::Continue: { Statement stmt{createWithLocation()}; checkBreakContinuePosition("continue"); - m_scanner->next(); + advance(); + return stmt; + } + case Token::Leave: + { + Statement stmt{createWithLocation()}; + if (!m_insideFunction) + m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); + advance(); return stmt; } - case Token::Identifier: - if (currentLiteral() == "leave") - { - Statement stmt{createWithLocation()}; - if (!m_insideFunction) - m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); - m_scanner->next(); - return stmt; - } - break; default: break; } @@ -284,12 +282,6 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() switch (currentToken()) { case Token::Identifier: - case Token::Return: - case Token::Byte: - case Token::Bool: - case Token::Address: - case Token::Var: - case Token::In: { YulString literal{currentLiteral()}; if (m_dialect.builtin(literal)) @@ -345,6 +337,9 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() ret = std::move(literal); break; } + case Token::HexStringLiteral: + fatalParserError(3772_error, "Hex literals are not valid in this context."); + break; default: fatalParserError(1856_error, "Literal or identifier expected."); } @@ -401,10 +396,9 @@ FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::Comma); } expectToken(Token::RParen); - if (currentToken() == Token::Sub) + if (currentToken() == Token::RightArrow) { - expectToken(Token::Sub); - expectToken(Token::GreaterThan); + expectToken(Token::RightArrow); while (true) { funDef.returnVariables.emplace_back(parseTypedName()); @@ -473,24 +467,10 @@ TypedName Parser::parseTypedName() YulString Parser::expectAsmIdentifier() { YulString name{currentLiteral()}; - switch (currentToken()) - { - case Token::Return: - case Token::Byte: - case Token::Address: - case Token::Bool: - case Token::Identifier: - case Token::Var: - case Token::In: - break; - default: - expectToken(Token::Identifier); - break; - } - - if (m_dialect.builtin(name)) + if (currentToken() == Token::Identifier && m_dialect.builtin(name)) fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name."); - advance(); + // NOTE: We keep the expectation here to ensure the correct source location for the error above. + expectToken(Token::Identifier); return name; } diff --git a/libyul/SideEffects.h b/libyul/SideEffects.h index e9c12d9c8..7433e40d2 100644 --- a/libyul/SideEffects.h +++ b/libyul/SideEffects.h @@ -18,6 +18,7 @@ #pragma once +#include #include namespace solidity::yul @@ -30,6 +31,20 @@ namespace solidity::yul */ struct SideEffects { + /// Corresponds to the effect that a Yul-builtin has on a generic data location (storage, memory + /// and other blockchain state). + enum Effect + { + None, + Read, + Write + }; + + friend Effect operator+(Effect const& _a, Effect const& _b) + { + return static_cast(std::max(static_cast(_a), static_cast(_b))); + } + /// If true, expressions in this code can be freely moved and copied without altering the /// semantics. /// At statement level, it means that functions containing this code can be @@ -38,22 +53,34 @@ struct SideEffects /// This means it cannot depend on storage or memory, cannot have any side-effects, /// but it can depend on state that is constant across an EVM-call. bool movable = true; + /// If true, the expressions in this code can be moved or copied (together with their arguments) + /// across control flow branches and instructions as long as these instructions' 'effects' do + /// not influence the 'effects' of the aforementioned expressions. + bool movableApartFromEffects = true; /// If true, the code can be removed without changing the semantics. - bool sideEffectFree = true; + bool canBeRemoved = true; /// If true, the code can be removed without changing the semantics as long as /// the whole program does not contain the msize instruction. - bool sideEffectFreeIfNoMSize = true; - /// If false, storage is guaranteed to be unchanged by the code under all - /// circumstances. - bool invalidatesStorage = false; - /// If false, memory is guaranteed to be unchanged by the code under all - /// circumstances. - bool invalidatesMemory = false; + bool canBeRemovedIfNoMSize = true; + /// If false, the code calls a for-loop or a recursive function, and therefore potentially loops + /// infinitely. All builtins are set to true by default, even `invalid()`. + bool cannotLoop = true; + /// Can write, read or have no effect on the blockchain state, when the value of `otherState` is + /// `Write`, `Read` or `None` respectively. + Effect otherState = None; + /// Can write, read or have no effect on storage, when the value of `storage` is `Write`, `Read` + /// or `None` respectively. When the value is `Write`, the expression can invalidate storage, + /// potentially indirectly through external calls. + Effect storage = None; + /// Can write, read or have no effect on memory, when the value of `memory` is `Write`, `Read` + /// or `None` respectively. Note that, when the value is `Read`, the expression can have an + /// effect on `msize()`. + Effect memory = None; /// @returns the worst-case side effects. static SideEffects worst() { - return SideEffects{false, false, false, true, true}; + return SideEffects{false, false, false, false, false, Write, Write, Write}; } /// @returns the combined side effects of two pieces of code. @@ -61,10 +88,13 @@ struct SideEffects { return SideEffects{ movable && _other.movable, - sideEffectFree && _other.sideEffectFree, - sideEffectFreeIfNoMSize && _other.sideEffectFreeIfNoMSize, - invalidatesStorage || _other.invalidatesStorage, - invalidatesMemory || _other.invalidatesMemory + movableApartFromEffects && _other.movableApartFromEffects, + canBeRemoved && _other.canBeRemoved, + canBeRemovedIfNoMSize && _other.canBeRemovedIfNoMSize, + cannotLoop && _other.cannotLoop, + otherState + _other.otherState, + storage + _other.storage, + memory + _other.memory }; } @@ -79,10 +109,13 @@ struct SideEffects { return movable == _other.movable && - sideEffectFree == _other.sideEffectFree && - sideEffectFreeIfNoMSize == _other.sideEffectFreeIfNoMSize && - invalidatesStorage == _other.invalidatesStorage && - invalidatesMemory == _other.invalidatesMemory; + movableApartFromEffects == _other.movableApartFromEffects && + canBeRemoved == _other.canBeRemoved && + canBeRemovedIfNoMSize == _other.canBeRemovedIfNoMSize && + cannotLoop == _other.cannotLoop && + otherState == _other.otherState && + storage == _other.storage && + memory == _other.memory; } }; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index f6affcdbf..dc648248b 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -190,7 +190,7 @@ map createBuiltins(langutil::EVMVersion _evmVe "datacopy", 3, 0, - SideEffects{false, false, false, false, true}, + SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, {}, []( FunctionCall const& _call, @@ -206,7 +206,7 @@ map createBuiltins(langutil::EVMVersion _evmVe "setimmutable", 2, 0, - SideEffects{false, false, false, false, true}, + SideEffects{false, false, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, {LiteralKind::String, std::nullopt}, []( FunctionCall const& _call, @@ -281,12 +281,20 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instruction) { + auto translate = [](evmasm::SemanticInformation::Effect _e) -> SideEffects::Effect + { + return static_cast(_e); + }; + return SideEffects{ evmasm::SemanticInformation::movable(_instruction), - evmasm::SemanticInformation::sideEffectFree(_instruction), - evmasm::SemanticInformation::sideEffectFreeIfNoMSize(_instruction), - evmasm::SemanticInformation::invalidatesStorage(_instruction), - evmasm::SemanticInformation::invalidatesMemory(_instruction) + evmasm::SemanticInformation::movableApartFromEffects(_instruction), + evmasm::SemanticInformation::canBeRemoved(_instruction), + evmasm::SemanticInformation::canBeRemovedIfNoMSize(_instruction), + true, // cannotLoop + translate(evmasm::SemanticInformation::otherState(_instruction)), + translate(evmasm::SemanticInformation::storage(_instruction)), + translate(evmasm::SemanticInformation::memory(_instruction)), }; } diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index fae7cd6cb..541f1f9f8 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -90,7 +90,7 @@ wasm::Expression WasmCodeTransform::generateMultiAssignment( return { std::move(block) }; } -wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDecl) +wasm::Expression WasmCodeTransform::operator()(yul::VariableDeclaration const& _varDecl) { vector variableNames; for (auto const& var: _varDecl.variables) @@ -105,7 +105,7 @@ wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDe return wasm::BuiltinCall{"nop", {}}; } -wasm::Expression WasmCodeTransform::operator()(Assignment const& _assignment) +wasm::Expression WasmCodeTransform::operator()(yul::Assignment const& _assignment) { vector variableNames; for (auto const& var: _assignment.variableNames) @@ -113,12 +113,12 @@ wasm::Expression WasmCodeTransform::operator()(Assignment const& _assignment) return generateMultiAssignment(move(variableNames), visit(*_assignment.value)); } -wasm::Expression WasmCodeTransform::operator()(ExpressionStatement const& _statement) +wasm::Expression WasmCodeTransform::operator()(yul::ExpressionStatement const& _statement) { return visitReturnByValue(_statement.expression); } -wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) +wasm::Expression WasmCodeTransform::operator()(yul::FunctionCall const& _call) { if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) { @@ -164,17 +164,17 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; } -wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier) +wasm::Expression WasmCodeTransform::operator()(yul::Identifier const& _identifier) { return wasm::LocalVariable{_identifier.name.str()}; } -wasm::Expression WasmCodeTransform::operator()(Literal const& _literal) +wasm::Expression WasmCodeTransform::operator()(yul::Literal const& _literal) { return makeLiteral(translatedType(_literal.type), valueOfLiteral(_literal)); } -wasm::Expression WasmCodeTransform::operator()(If const& _if) +wasm::Expression WasmCodeTransform::operator()(yul::If const& _if) { yul::Type conditionType = m_typeInfo.typeOf(*_if.condition); @@ -196,7 +196,7 @@ wasm::Expression WasmCodeTransform::operator()(If const& _if) return wasm::If{make_unique(move(condition)), visit(_if.body.statements), {}}; } -wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) +wasm::Expression WasmCodeTransform::operator()(yul::Switch const& _switch) { yul::Type expressionType = m_typeInfo.typeOf(*_switch.expression); YulString eq_instruction = YulString(expressionType.str() + ".eq"); @@ -240,13 +240,13 @@ wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) return { std::move(block) }; } -wasm::Expression WasmCodeTransform::operator()(FunctionDefinition const&) +wasm::Expression WasmCodeTransform::operator()(yul::FunctionDefinition const&) { yulAssert(false, "Should not have visited here."); return {}; } -wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) +wasm::Expression WasmCodeTransform::operator()(yul::ForLoop const& _for) { string breakLabel = newLabel(); string continueLabel = newLabel(); @@ -273,23 +273,25 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) return wasm::Block{breakLabel, move(statements)}; } -wasm::Expression WasmCodeTransform::operator()(Break const&) +wasm::Expression WasmCodeTransform::operator()(yul::Break const&) { + yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}}; } -wasm::Expression WasmCodeTransform::operator()(Continue const&) +wasm::Expression WasmCodeTransform::operator()(yul::Continue const&) { + yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}}; } -wasm::Expression WasmCodeTransform::operator()(Leave const&) +wasm::Expression WasmCodeTransform::operator()(yul::Leave const&) { yulAssert(!m_functionBodyLabel.empty(), ""); return wasm::Branch{wasm::Label{m_functionBodyLabel}}; } -wasm::Expression WasmCodeTransform::operator()(Block const& _block) +wasm::Expression WasmCodeTransform::operator()(yul::Block const& _block) { return wasm::Block{{}, visit(_block.statements)}; } diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 6d2bf8524..1c7755d6a 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -86,27 +86,34 @@ WasmDialect::WasmDialect() addFunction("i64.extend_i32_u", {i32}, {i64}); addFunction("i32.store", {i32, i32}, {}, false); - m_functions["i32.store"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.store"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.store"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i64.store", {i32, i64}, {}, false); - m_functions["i64.store"_yulstring].sideEffects.invalidatesStorage = false; // TODO: add i32.store16, i64.store8, i64.store16, i64.store32 + m_functions["i64.store"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.store"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i32.store8", {i32, i32}, {}, false); - m_functions["i32.store8"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.store8"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.store8"_yulstring].sideEffects.otherState = SideEffects::None; + addFunction("i64.store8", {i32, i64}, {}, false); - m_functions["i64.store8"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i64.store8"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.store8"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i32.load", {i32}, {i32}, false); - m_functions["i32.load"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["i32.load"_yulstring].sideEffects.invalidatesMemory = false; - m_functions["i32.load"_yulstring].sideEffects.sideEffectFree = true; - m_functions["i32.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; + m_functions["i32.load"_yulstring].sideEffects.canBeRemoved = true; + m_functions["i32.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true; + m_functions["i32.load"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.load"_yulstring].sideEffects.memory = SideEffects::Read; + m_functions["i32.load"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i64.load", {i32}, {i64}, false); - m_functions["i64.load"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["i64.load"_yulstring].sideEffects.invalidatesMemory = false; - m_functions["i64.load"_yulstring].sideEffects.sideEffectFree = true; - m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; // TODO: add i32.load8, i32.load16, i64.load8, i64.load16, i64.load32 + m_functions["i64.load"_yulstring].sideEffects.canBeRemoved = true; + m_functions["i64.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true; + m_functions["i64.load"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.load"_yulstring].sideEffects.memory = SideEffects::Read; + m_functions["i64.load"_yulstring].sideEffects.otherState = SideEffects::None; // Drop is actually overloaded for all types, but Yul does not support that. // Because of that, we introduce "i32.drop" and "i64.drop". @@ -115,8 +122,9 @@ WasmDialect::WasmDialect() addFunction("nop", {}, {}); addFunction("unreachable", {}, {}, false); - m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; + m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None; + m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None; m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true; m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true; @@ -219,10 +227,24 @@ void WasmDialect::addEthereumExternals() f.returns.emplace_back(YulString(p)); // TODO some of them are side effect free. f.sideEffects = SideEffects::worst(); + f.sideEffects.cannotLoop = true; + f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminates; f.controlFlowSideEffects = ext.controlFlowSideEffects; f.isMSize = false; - f.sideEffects.invalidatesStorage = (ext.name == "storageStore"); f.literalArguments.clear(); + + static set const writesToStorage{ + "storageStore", + "call", + "callcode", + "callDelegate", + "create" + }; + static set const readsStorage{"storageLoad", "callStatic"}; + if (readsStorage.count(ext.name)) + f.sideEffects.storage = SideEffects::Read; + else if (!writesToStorage.count(ext.name)) + f.sideEffects.storage = SideEffects::None; } } @@ -241,6 +263,9 @@ void WasmDialect::addFunction( yulAssert(_returns.size() <= 1, "The Wasm 1.0 specification only allows up to 1 return value."); f.returns = std::move(_returns); f.sideEffects = _movable ? SideEffects{} : SideEffects::worst(); + f.sideEffects.cannotLoop = true; + // TODO This should be improved when LoopInvariantCodeMotion gets specialized for WASM + f.sideEffects.movableApartFromEffects = _movable; f.isMSize = false; f.literalArguments = std::move(_literalArguments); } diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 2e9dd1e05..cd17b7aff 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -80,10 +80,20 @@ void FullInliner::run() // TODO it might be good to determine a visiting order: // first handle functions that are called from many places. - for (auto const& fun: m_functions) + + // Note that the order of inlining can result in very different code. + // Since AST IDs and thus function names depend on whether or not a contract + // is compiled together with other source files, a change in AST IDs + // should have as little an impact as possible. This is the case + // if we handle inlining in source (and thus, for the IR generator, + // function name) order. + for (auto& statement: m_ast.statements) { - handleBlock(fun.second->name, fun.second->body); - updateCodeSize(*fun.second); + if (!holds_alternative(statement)) + continue; + FunctionDefinition& fun = std::get(statement); + handleBlock(fun.name, fun.body); + updateCodeSize(fun); } } diff --git a/libyul/optimiser/LoopInvariantCodeMotion.cpp b/libyul/optimiser/LoopInvariantCodeMotion.cpp index 9a81e5fc8..3cc8397a9 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.cpp +++ b/libyul/optimiser/LoopInvariantCodeMotion.cpp @@ -35,9 +35,9 @@ void LoopInvariantCodeMotion::run(OptimiserStepContext& _context, Block& _ast) { map functionSideEffects = SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)); - + bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast); set ssaVars = SSAValueTracker::ssaVariables(_ast); - LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects}(_ast); + LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects, containsMSize}(_ast); } void LoopInvariantCodeMotion::operator()(Block& _block) @@ -57,7 +57,8 @@ void LoopInvariantCodeMotion::operator()(Block& _block) bool LoopInvariantCodeMotion::canBePromoted( VariableDeclaration const& _varDecl, - set const& _varsDefinedInCurrentScope + set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects ) const { // A declaration can be promoted iff @@ -73,7 +74,8 @@ bool LoopInvariantCodeMotion::canBePromoted( for (auto const& ref: ReferencesCounter::countReferences(*_varDecl.value, ReferencesCounter::OnlyVariables)) if (_varsDefinedInCurrentScope.count(ref.first) || !m_ssaVariables.count(ref.first)) return false; - if (!SideEffectsCollector{m_dialect, *_varDecl.value, &m_functionSideEffects}.movable()) + SideEffectsCollector sideEffects{m_dialect, *_varDecl.value, &m_functionSideEffects}; + if (!sideEffects.movableRelativeTo(_forLoopSideEffects, m_containsMSize)) return false; } return true; @@ -82,6 +84,10 @@ bool LoopInvariantCodeMotion::canBePromoted( optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) { assertThrow(_for.pre.statements.empty(), OptimizerException, ""); + + auto forLoopSideEffects = + SideEffectsCollector{m_dialect, _for, &m_functionSideEffects}.sideEffects(); + vector replacement; for (Block* block: {&_for.post, &_for.body}) { @@ -93,7 +99,7 @@ optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) if (holds_alternative(_s)) { VariableDeclaration const& varDecl = std::get(_s); - if (canBePromoted(varDecl, varsDefinedInScope)) + if (canBePromoted(varDecl, varsDefinedInScope, forLoopSideEffects)) { replacement.emplace_back(std::move(_s)); // Do not add the variables declared here to varsDefinedInScope because we are moving them. diff --git a/libyul/optimiser/LoopInvariantCodeMotion.h b/libyul/optimiser/LoopInvariantCodeMotion.h index a3bf1784f..1b2647924 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.h +++ b/libyul/optimiser/LoopInvariantCodeMotion.h @@ -49,17 +49,24 @@ private: explicit LoopInvariantCodeMotion( Dialect const& _dialect, std::set const& _ssaVariables, - std::map const& _functionSideEffects + std::map const& _functionSideEffects, + bool _containsMSize ): + m_containsMSize(_containsMSize), m_dialect(_dialect), m_ssaVariables(_ssaVariables), m_functionSideEffects(_functionSideEffects) { } /// @returns true if the given variable declaration can be moved to in front of the loop. - bool canBePromoted(VariableDeclaration const& _varDecl, std::set const& _varsDefinedInCurrentScope) const; + bool canBePromoted( + VariableDeclaration const& _varDecl, + std::set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects + ) const; std::optional> rewriteLoop(ForLoop& _for); + bool m_containsMSize = true; Dialect const& m_dialect; std::set const& m_ssaVariables; std::map const& m_functionSideEffects; diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index ec091b2d7..eaf9b8b4b 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -22,8 +22,10 @@ #include #include +#include #include #include +#include #include @@ -57,7 +59,5 @@ YulString NameDispenser::newName(YulString _nameHint) bool NameDispenser::illegalName(YulString _name) { - if (_name.empty() || m_usedNames.count(_name) || m_dialect.builtin(_name)) - return true; - return false; + return isRestrictedIdentifier(m_dialect, _name) || m_usedNames.count(_name); } diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp index 3cb66d689..ad46f7cb1 100644 --- a/libyul/optimiser/OptimizerUtilities.cpp +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -21,15 +21,19 @@ #include +#include #include +#include #include #include using namespace std; using namespace solidity; +using namespace solidity::langutil; using namespace solidity::util; +using namespace solidity::yul; void yul::removeEmptyBlocks(Block& _block) { @@ -38,3 +42,8 @@ void yul::removeEmptyBlocks(Block& _block) }; boost::range::remove_erase_if(_block.statements, isEmptyBlock); } + +bool yul::isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifier) +{ + return _identifier.empty() || TokenTraits::isYulKeyword(_identifier.str()) || _dialect.builtin(_identifier); +} diff --git a/libyul/optimiser/OptimizerUtilities.h b/libyul/optimiser/OptimizerUtilities.h index 61d970877..829d10b55 100644 --- a/libyul/optimiser/OptimizerUtilities.h +++ b/libyul/optimiser/OptimizerUtilities.h @@ -23,6 +23,8 @@ #include #include +#include +#include namespace solidity::yul { @@ -30,4 +32,8 @@ namespace solidity::yul /// Removes statements that are just empty blocks (non-recursive). void removeEmptyBlocks(Block& _block); +/// Returns true if a given literal can not be used as an identifier. +/// This includes Yul keywords and builtins of the given dialect. +bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifier); + } diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 4a7b8db12..735ed13d0 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -62,6 +62,16 @@ SideEffectsCollector::SideEffectsCollector( operator()(_ast); } +SideEffectsCollector::SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + map const* _functionSideEffects +): + SideEffectsCollector(_dialect, _functionSideEffects) +{ + operator()(_ast); +} + void SideEffectsCollector::operator()(FunctionCall const& _functionCall) { ASTWalker::operator()(_functionCall); @@ -106,8 +116,9 @@ map SideEffectsPropagator::sideEffects( for (auto const& function: _directCallGraph.functionsWithLoops + _directCallGraph.recursiveFunctions()) { ret[function].movable = false; - ret[function].sideEffectFree = false; - ret[function].sideEffectFreeIfNoMSize = false; + ret[function].canBeRemoved = false; + ret[function].canBeRemovedIfNoMSize = false; + ret[function].cannotLoop = false; } for (auto const& call: _directCallGraph.functionCalls) diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index a4f7b8102..70ec119ac 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -54,21 +54,60 @@ public: Block const& _ast, std::map const* _functionSideEffects = nullptr ); + SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + std::map const* _functionSideEffects = nullptr + ); using ASTWalker::operator(); void operator()(FunctionCall const& _functionCall) override; bool movable() const { return m_sideEffects.movable; } - bool sideEffectFree(bool _allowMSizeModification = false) const + + bool movableRelativeTo(SideEffects const& _other, bool _codeContainsMSize) + { + if (!m_sideEffects.cannotLoop) + return false; + + if (m_sideEffects.movable) + return true; + + if ( + !m_sideEffects.movableApartFromEffects || + m_sideEffects.storage == SideEffects::Write || + m_sideEffects.otherState == SideEffects::Write || + m_sideEffects.memory == SideEffects::Write + ) + return false; + + if (m_sideEffects.otherState == SideEffects::Read) + if (_other.otherState == SideEffects::Write) + return false; + + if (m_sideEffects.storage == SideEffects::Read) + if (_other.storage == SideEffects::Write) + return false; + + if (m_sideEffects.memory == SideEffects::Read) + if (_codeContainsMSize || _other.memory == SideEffects::Write) + return false; + + return true; + } + + bool canBeRemoved(bool _allowMSizeModification = false) const { if (_allowMSizeModification) - return sideEffectFreeIfNoMSize(); + return m_sideEffects.canBeRemovedIfNoMSize; else - return m_sideEffects.sideEffectFree; + return m_sideEffects.canBeRemoved; } - bool sideEffectFreeIfNoMSize() const { return m_sideEffects.sideEffectFreeIfNoMSize; } - bool invalidatesStorage() const { return m_sideEffects.invalidatesStorage; } - bool invalidatesMemory() const { return m_sideEffects.invalidatesMemory; } + bool cannotLoop() const { return m_sideEffects.cannotLoop; } + bool invalidatesStorage() const { return m_sideEffects.storage == SideEffects::Write; } + bool invalidatesMemory() const { return m_sideEffects.memory == SideEffects::Write; } + + SideEffects sideEffects() { return m_sideEffects; } private: Dialect const& m_dialect; diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index a0e0f535d..3779c0d63 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -93,7 +93,7 @@ void UnusedPruner::operator()(Block& _block) statement = Block{std::move(varDecl.location), {}}; else if ( SideEffectsCollector(m_dialect, *varDecl.value, m_functionSideEffects). - sideEffectFree(m_allowMSizeOptimization) + canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); @@ -112,7 +112,7 @@ void UnusedPruner::operator()(Block& _block) ExpressionStatement& exprStmt = std::get(statement); if ( SideEffectsCollector(m_dialect, exprStmt.expression, m_functionSideEffects). - sideEffectFree(m_allowMSizeOptimization) + canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index edea33d2b..85ecece46 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include +#include #include #include @@ -110,9 +111,7 @@ YulString VarNameCleaner::findCleanName(YulString const& _name) const bool VarNameCleaner::isUsedName(YulString const& _name) const { - if (_name.empty() || m_dialect.builtin(_name) || m_usedNames.count(_name)) - return true; - return false; + return isRestrictedIdentifier(m_dialect, _name) || m_usedNames.count(_name); } YulString VarNameCleaner::stripSuffix(YulString const& _name) const diff --git a/scripts/ci/build_ossfuzz.sh b/scripts/ci/build_ossfuzz.sh index 42f7b3a39..fc7edf349 100755 --- a/scripts/ci/build_ossfuzz.sh +++ b/scripts/ci/build_ossfuzz.sh @@ -1,15 +1,55 @@ #!/usr/bin/env bash -set -e +set -ex -ROOTDIR="$(dirname "$0")/../.." +ROOTDIR="/root/project" BUILDDIR="${ROOTDIR}/build" +mkdir -p "${BUILDDIR}" && mkdir -p "$BUILDDIR/deps" -mkdir -p "${BUILDDIR}" -cd "${BUILDDIR}" +ANTLRJAR="${ROOTDIR}/build/deps/antlr4.8.jar" +ANTLRJAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" -protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz -protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz -protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz -cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/libfuzzer.cmake +download_antlr4() +{ + if [[ ! -e "${ANTLRJAR}" ]] + then + wget -O "${ANTLRJAR}" "${ANTLRJAR_URI}" + fi +} -make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 +generate_protobuf_bindings() +{ + cd "${ROOTDIR}"/test/tools/ossfuzz + # Generate protobuf C++ bindings + for protoName in yul abiV2 sol; + do + protoc "${protoName}"Proto.proto --cpp_out . + done +} + +generate_antlr4_bindings() +{ + cd "${ROOTDIR}" + # Replace boolean with bool to suit c++ syntax + sed -i 's/boolean /bool /g' docs/grammar/Solidity.g4 + # Generate antlr4 visitor/parser/lexer c++ bindings + java -jar "${ANTLRJAR}" -Dlanguage=Cpp \ + -Xexact-output-dir -package solidity::test::fuzzer -o test/tools/ossfuzz \ + -no-listener -visitor docs/grammar/SolidityLexer.g4 docs/grammar/Solidity.g4 + # Delete unnecessary autogen files + rm -f "${ROOTDIR}"/test/tools/ossfuzz/Solidity*Visitor.cpp \ + "${ROOTDIR}"/test/tools/ossfuzz/Solidity*.interp \ + "${ROOTDIR}"/test/tools/ossfuzz/Solidity*.tokens +} + +build_fuzzers() +{ + cd "${BUILDDIR}" + cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ + -DCMAKE_TOOLCHAIN_FILE="${ROOTDIR}"/cmake/toolchains/libfuzzer.cmake + make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 +} + +download_antlr4 +generate_protobuf_bindings +generate_antlr4_bindings +build_fuzzers \ No newline at end of file diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index 32f84573c..c30573fe1 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="2" +LABEL version="3" ARG DEBIAN_FRONTEND=noninteractive @@ -31,7 +31,8 @@ RUN apt-get update; \ build-essential \ software-properties-common \ ninja-build git wget \ - libbz2-dev zlib1g-dev git curl; \ + libbz2-dev zlib1g-dev git curl uuid-dev \ + pkg-config openjdk-8-jdk liblzma-dev unzip; \ apt-get install -qy python-pip python-sphinx; # Install cmake 3.14 (minimum requirement is cmake 3.10) @@ -108,6 +109,24 @@ RUN set -ex; \ ninja install/strip; \ rm -rf /usr/src/hera +# ANTLR4 RUNTIME +RUN set -ex; \ + cd /usr/src; \ + wget https://www.antlr.org/download/antlr4-cpp-runtime-4.8-source.zip; \ + rm -rf antlr4-runtime && mkdir antlr4-runtime; \ + unzip antlr4-cpp-runtime-4.8-source.zip -d antlr4-runtime; \ + cd antlr4-runtime && mkdir build && cd build; \ + cmake .. -DWITH_LIBCXX=On -DCMAKE_BUILD_TYPE=Release -DWITH_DEMO=False; \ + make -j; \ + DESTDIR=run make install; \ + # Manually copy needed library and includes since install script + # does not respect -DCMAKE_INSTALL_DIR and there is no option + # to disable shared library build/installation + cp -Rf run/usr/local/include/antlr4-runtime /usr/include; \ + cp -f run/usr/local/lib/libantlr4-runtime.a /usr/lib; \ + rm -rf /usr/src/antlr4-cpp-runtime-4.8-source.zip; \ + rm -rf /usr/src/antlr4-runtime + FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 6b28b1fc6..69e452b2a 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -12,8 +12,10 @@ SOURCE_FILE_PATTERN = r"\b\d+_error\b" def read_file(file_name): content = None + _, tail = path.split(file_name) + is_latin = tail == "invalid_utf8_sequence.sol" try: - with open(file_name, "r", encoding=ENCODING) as f: + with open(file_name, "r", encoding="latin-1" if is_latin else ENCODING) as f: content = f.read() finally: if content == None: @@ -217,15 +219,15 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): return False old_source_only_ids = { - "1054", "1123", "1133", "1220", "1584", "1823", "1950", "1957", + "1123", "1133", "1220", "1584", "1823", "1950", "1988", "2418", "2461", "2512", "2592", "2657", "2800", "2842", "2856", - "3263", "3299", "3356", "3441", "3682", "3876", - "3893", "3997", "4010", "4110", "4802", "4805", "4828", + "3263", "3356", "3441", "3682", "3876", + "3893", "3997", "4010", "4802", "4805", "4828", "4904", "4990", "5052", "5073", "5170", "5188", "5272", "5333", "5347", "5473", "5622", "6041", "6052", "6272", "6708", "6792", "6931", "7110", "7128", "7186", "7319", "7589", "7593", "7653", "7812", "7885", "8065", "8084", "8140", - "8261", "8312", "8452", "8592", "8758", "9011", - "9085", "9102", "9390", "9439", "9440", "9547", "9551", "9615", "9980" + "8261", "8312", "8592", "8758", "9011", + "9085", "9390", "9440", "9547", "9551", "9615", "9980" } new_source_only_ids = source_only_ids - old_source_only_ids if len(new_source_only_ids) != 0: diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index a825a6d84..87f889e8f 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -101,7 +101,7 @@ case $(uname -s) in esac # Check for Homebrew install and abort if it is not installed. - brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See http://brew.sh."; exit 1; } + brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See https://brew.sh."; exit 1; } brew update brew install boost brew install cmake diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 0f2bc1d79..33fdc0165 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -10,7 +10,7 @@ import sys import re import os import hashlib -from os.path import join, isfile +from os.path import join, isfile, split def extract_test_cases(path): lines = open(path, encoding="utf8", errors='ignore', mode='r').read().splitlines() @@ -43,7 +43,7 @@ def extract_docs_cases(path): tests = [] # Collect all snippets of indented blocks - for l in open(path, mode='r', encoding='utf8').read().splitlines(): + for l in open(path, mode='r', errors='ignore', encoding='utf8').read().splitlines(): if l != '': if not inside and l.startswith(' '): # start new test @@ -99,5 +99,8 @@ if __name__ == '__main__': if 'compilationTests' in subdirs: subdirs.remove('compilationTests') for f in files: + _, tail = split(f) + if tail == "invalid_utf8_sequence.sol": + continue # ignore the test with broken utf-8 encoding path = join(root, f) extract_and_write(f, path) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 97cbe4ab4..846cecf80 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -2,11 +2,10 @@ set -e -ROOT_DIR="$(dirname "$0")"/.. +ROOT_DIR=$(readlink -f "$(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")" +ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" SGR_RESET="\033[0m" SGR_BOLD="\033[1m" @@ -40,11 +39,14 @@ if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \ [ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ] then echo "Creating parser" + ( + cd "${ROOT_DIR}"/docs/grammar # Create lexer/parser from grammar - java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/" + java -jar "${ANTLR_JAR}" Solidity.g4 SolidityLexer.g4 -o "${WORKDIR}/src/" # Compile lexer/parser sources javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" + ) fi # Run tests @@ -55,47 +57,87 @@ test_file() SOL_FILE="$(readlink -m "${1}")" local cur=${2} local max=${3} + local solOrYul=${4} 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 - ) + if [[ "${solOrYul}" == "sol" ]]; then + output=$( + java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + sourceUnit <"${SOL_FILE}" 2>&1 + ) + else + output=$( + echo "assembly $(cat "${SOL_FILE}")" | java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + assemblyStatement 2>&1 + ) + fi 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}" + if grep -qE "^\/\/ ParserError" "${SOL_FILE}"; then + if [[ "${output}" != "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}SUCCEEDED DESPITE PARSER ERROR${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi 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 + 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 fi } -# we only want to use files that do not contain errors or multi-source files. +# we only want to use files that do not contain excluded parser errors, analysis 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:" \ + "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (6275|3716|6281|2837|6933)|^==== Source:" \ "${ROOT_DIR}/test/libsolidity/syntaxTests" \ "${ROOT_DIR}/test/libsolidity/semanticTests" \ ) +YUL_FILES=() +# Add all yul optimizer tests without objects and types. +while IFS='' read -r line +do + YUL_FILES+=("$line") +done < <( + grep -riL -E \ + "object|\:[ ]*[uib]" \ + "${ROOT_DIR}/test/libyul/yulOptimizerTests" +) + +num_tests=$((${#SOL_FILES[*]} + ${#YUL_FILES[*]})) test_count=0 for SOL_FILE in "${SOL_FILES[@]}" do test_count=$((test_count + 1)) - test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]} + test_file "${SOL_FILE}" ${test_count} $num_tests "sol" +done +for YUL_FILE in "${YUL_FILES[@]}" +do + test_count=$((test_count + 1)) + test_file "${YUL_FILE}" ${test_count} $num_tests "yul" done -echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed." +echo "Summary: ${failed_count} of $num_tests sources failed." exit ${failed_count} diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index b3841f6b4..7f5dfcf74 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -97,12 +97,15 @@ u256 ExecutionFramework::gasLimit() const u256 ExecutionFramework::gasPrice() const { - return {EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; + // here and below we use "return u256{....}" instead of just "return {....}" + // to please MSVC and avoid unexpected + // warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied + return u256{EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; } u256 ExecutionFramework::blockHash(u256 const& _number) const { - return {EVMHost::convertFromEVMC( + return u256{EVMHost::convertFromEVMC( m_evmHost->get_block_hash(static_cast(_number & numeric_limits::max())) )}; } diff --git a/test/cmdlineTests/optimizer_array_sload/args b/test/cmdlineTests/optimizer_array_sload/args new file mode 100644 index 000000000..8539e69f5 --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/args @@ -0,0 +1 @@ +--optimize --ir-optimized --metadata-hash none diff --git a/test/cmdlineTests/optimizer_array_sload/input.sol b/test/cmdlineTests/optimizer_array_sload/input.sol new file mode 100644 index 000000000..1ad596601 --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract Arraysum { + uint256[] values; + + function sumArray() public view returns(uint) { + uint sum = 0; + // The optimizer should read the length of the array only once, because + // LoopInvariantCodeMotion can move the `sload` corresponding to the length outside of the + // loop. + for(uint i = 0; i < values.length; i++) + sum += values[i]; + } +} diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output new file mode 100644 index 000000000..ea4ae03ff --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -0,0 +1,83 @@ +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "Arraysum_33" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("Arraysum_33_deployed") + codecopy(0, dataoffset("Arraysum_33_deployed"), _1) + return(0, _1) + } + } + object "Arraysum_33_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x81d73423, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + let vloc_sum := _1 + let vloc_i := _1 + let _2 := sload(_1) + for { } + lt(vloc_i, _2) + { + vloc_i := increment_t_uint256(vloc_i) + } + { + let _3, _4 := storage_array_index_access_t_array$_t_uint256_$dyn_storage(_1, vloc_i) + vloc_sum := checked_add_t_uint256(vloc_sum, extract_from_storage_value_dynamict_uint256(sload(_3), _4)) + } + let memPos := allocateMemory(_1) + return(memPos, sub(abi_encode_tuple_t_uint256__to_t_uint256__fromStack(memPos, _1), memPos)) + } + } + revert(0, 0) + } + function abi_encode_tuple_t_uint256__to_t_uint256__fromStack(headStart, value0) -> tail + { + tail := add(headStart, 32) + mstore(headStart, value0) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + mstore(64, newFreePtr) + } + function checked_add_t_uint256(x, y) -> sum + { + if gt(x, not(y)) { revert(sum, sum) } + sum := add(x, y) + } + function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value + { + value := shr(mul(offset, 8), slot_value) + } + function increment_t_uint256(value) -> ret + { + if gt(value, not(1)) { revert(ret, ret) } + ret := add(value, 1) + } + function storage_array_index_access_t_array$_t_uint256_$dyn_storage(array, index) -> slot, offset + { + if iszero(lt(index, sload(array))) { invalid() } + mstore(slot, array) + slot := add(keccak256(slot, 0x20), index) + offset := offset + } + } + } +} diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output index 34f35c643..9a4301aa4 100644 --- a/test/cmdlineTests/optimizer_user_yul/output +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -48,19 +48,22 @@ tag_5: 0x02 /* "optimizer_user_yul/input.sol":359:371 sstore(2, 3) */ sstore - /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ -tag_6: /* "optimizer_user_yul/input.sol":390:391 5 */ 0x05 /* "optimizer_user_yul/input.sol":384:392 sload(5) */ sload - tag_9 - jumpi - jump(tag_8) -tag_9: + iszero /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ +tag_6: + /* "optimizer_user_yul/input.sol":384:392 sload(5) */ + dup1 + /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ + tag_8 + jumpi jump(tag_6) tag_8: + /* "optimizer_user_yul/input.sol":380:383 { } */ + pop /* "optimizer_user_yul/input.sol":340:513 {... */ pop /* "optimizer_user_yul/input.sol":60:518 contract C... */ diff --git a/test/cmdlineTests/recovery_ast_empty_contract/err b/test/cmdlineTests/recovery_ast_empty_contract/err index 588067877..ab976c155 100644 --- a/test/cmdlineTests/recovery_ast_empty_contract/err +++ b/test/cmdlineTests/recovery_ast_empty_contract/err @@ -1,4 +1,4 @@ -Error: Expected pragma, import directive or contract/interface/library/struct/enum definition. +Error: Expected pragma, import directive or contract/interface/library/struct/enum/function definition. --> recovery_ast_empty_contract/input.sol:3:1: | 3 | c diff --git a/test/cmdlineTests/recovery_standard_json/output.json b/test/cmdlineTests/recovery_standard_json/output.json index a4113e186..7aa7214e4 100644 --- a/test/cmdlineTests/recovery_standard_json/output.json +++ b/test/cmdlineTests/recovery_standard_json/output.json @@ -4,4 +4,4 @@ pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name ","message":"Expected type name","severity":"error","sourceLocation":{"end":94,"file":"A","start":93},"type":"ParserError"},{"component":"general","errorCode":"3796","formattedMessage":"A:2:84: Warning: Recovered in ContractDefinition at '}'. pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ } ^ -","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}} +","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index a3885d128..7b2af14bf 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -6,6 +6,9 @@ (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) (memory $memory (export \"memory\") 1) (export \"main\" (func $main)) + (global $global_ (mut i64) (i64.const 0)) + (global $global__1 (mut i64) (i64.const 0)) + (global $global__2 (mut i64) (i64.const 0)) (func $main (local $_1 i64) @@ -15,6 +18,7 @@ (local $z1 i64) (local $z2 i64) (local $z3 i64) + (local $z4 i64) (local $_3 i64) (block $label_ (local.set $_1 (i64.const 0)) @@ -28,14 +32,18 @@ (i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128))) (call $eth.getCallValue (i32.const 0)) - (local.set $z1 (call $endian_swap (i64.load (i32.const 0)))) - (local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8))))) - (local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16))))) - (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then - (call $revert (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)))) + (block + (local.set $z1 (call $mload_internal (i32.const 0))) + (local.set $z2 (global.get $global_)) + (local.set $z3 (global.get $global__1)) + (local.set $z4 (global.get $global__2)) + + ) + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (local.get $z4))))) (then + (call $eth.revert (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))))) (local.set $_3 (datasize \"C_2_deployed\")) - (call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) - (call $return (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) + (call $eth.codeCopy (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\")) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))) + (call $eth.finish (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))) ) ) @@ -46,7 +54,7 @@ (param $x4 i64) (result i32) (local $v i32) - (block $label__1 + (block $label__3 (if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then (unreachable))) (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then @@ -65,7 +73,7 @@ (result i32) (local $r i32) (local $p i32) - (block $label__2 + (block $label__4 (local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (local.set $r (i32.add (local.get $p) (i32.const 64))) (if (i32.lt_u (local.get $r) (local.get $p)) (then @@ -75,29 +83,11 @@ (local.get $r) ) -(func $codecopy - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) - (param $z1 i64) - (param $z2 i64) - (param $z3 i64) - (param $z4 i64) - (block $label__3 - (call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4))) - ) -) - (func $endian_swap_16 (param $x i64) (result i64) (local $y i64) - (block $label__4 + (block $label__5 (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) ) @@ -109,7 +99,7 @@ (result i64) (local $y i64) (local $hi i64) - (block $label__5 + (block $label__6 (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) @@ -122,7 +112,7 @@ (result i64) (local $y i64) (local $hi i64) - (block $label__6 + (block $label__7 (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) @@ -130,32 +120,24 @@ (local.get $y) ) -(func $return - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) - (block $label__7 - (call $eth.finish (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))) - ) -) - -(func $revert - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) +(func $mload_internal + (param $pos i32) + (result i64) + (local $z1 i64) + (local $z2 i64) + (local $z3 i64) + (local $z4 i64) (block $label__8 - (call $eth.revert (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))) + (local.set $z1 (call $endian_swap (i64.load (local.get $pos)))) + (local.set $z2 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 8))))) + (local.set $z3 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 16))))) + (local.set $z4 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 24))))) + ) + (global.set $global_ (local.get $z2)) + (global.set $global__1 (local.get $z3)) + (global.set $global__2 (local.get $z4)) + (local.get $z1) ) ) diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json index c1ffe5892..16f8bfddb 100644 --- a/test/cmdlineTests/standard_only_ast_requested/output.json +++ b/test/cmdlineTests/standard_only_ast_requested/output.json @@ -1 +1 @@ -{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"documentation":null,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} +{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} diff --git a/test/liblangutil/Scanner.cpp b/test/liblangutil/Scanner.cpp index bbdf2a469..2110cb2b6 100644 --- a/test/liblangutil/Scanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -406,7 +406,7 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_CHECK_EQUAL(scanner.next(), Token::AssignAdd); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Inc); - BOOST_CHECK_EQUAL(scanner.next(), Token::Arrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::DoubleArrow); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); BOOST_CHECK_EQUAL(scanner.next(), Token::SAR); BOOST_CHECK_EQUAL(scanner.next(), Token::AssignSar); @@ -589,6 +589,42 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) // Unicode string literal +BOOST_AUTO_TEST_CASE(unicode_prefix_only) +{ + Scanner scanner(CharStream("{ unicode", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ unicode", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); +} + +BOOST_AUTO_TEST_CASE(unicode_invalid_space) +{ + Scanner scanner(CharStream("{ unicode ", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(unicode_invalid_token) +{ + Scanner scanner(CharStream("{ unicode test", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ unicode test", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "test"); +} + BOOST_AUTO_TEST_CASE(valid_unicode_literal) { Scanner scanner(CharStream("{ unicode\"Hello 😃\"", "")); @@ -606,7 +642,40 @@ BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \x07\xf0\x9f\x98\x83", 11)); } -// HEX STRING LITERAL +// Hex string literal + +BOOST_AUTO_TEST_CASE(hex_prefix_only) +{ + Scanner scanner(CharStream("{ hex", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ hex", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(hex_invalid_space) +{ + Scanner scanner(CharStream("{ hex ", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(hex_invalid_token) +{ + Scanner scanner(CharStream("{ hex test", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ hex test", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} BOOST_AUTO_TEST_CASE(valid_hex_literal) { @@ -648,7 +717,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } -// COMMENTS +// Comments BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { @@ -771,6 +840,137 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) } } +BOOST_AUTO_TEST_CASE(solidity_keywords) +{ + // These are tokens which have a different meaning in Yul. + string keywords = "return byte bool address var in true false leave switch case default"; + Scanner scanner(CharStream(keywords, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); + BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); + BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); + BOOST_CHECK_EQUAL(scanner.next(), Token::Address); + BOOST_CHECK_EQUAL(scanner.next(), Token::Var); + BOOST_CHECK_EQUAL(scanner.next(), Token::In); + BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); + BOOST_CHECK_EQUAL(scanner.next(), Token::Case); + BOOST_CHECK_EQUAL(scanner.next(), Token::Default); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(keywords, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::Leave); + BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); + BOOST_CHECK_EQUAL(scanner.next(), Token::Case); + BOOST_CHECK_EQUAL(scanner.next(), Token::Default); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_keyword_like) +{ + Scanner scanner(CharStream("leave.function", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("leave.function", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) +{ + Scanner scanner(CharStream("mystorage.slot := 1", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("mystorage.slot := 1", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_function) +{ + string sig = "function f(a, b) -> x, y"; + Scanner scanner(CharStream(sig, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::RightArrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(sig, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::RightArrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) +{ + string sig = "function f (a, b) - > x, y"; + Scanner scanner(CharStream(sig, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(sig, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json index 4284c4f8f..d09d9b10e 100644 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -1,3 +1,124 @@ +[ +{ + "attributes": + { + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 2 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 2 + ], + "name": "C", + "nodes": + [ + null + ], + "scope": 3 + }, + "children": + [ + { + "attributes": + { + "text": "This contract is empty" + }, + "id": 1, + "name": "StructuredDocumentation", + "src": "0:27:1" + } + ], + "id": 2, + "name": "ContractDefinition", + "src": "28:13:1" + } + ], + "id": 3, + "name": "SourceUnit", + "src": "28:14:1" +}, +{ + "attributes": + { + "absolutePath": "b", + "exportedSymbols": + { + "C": + [ + 5 + ] + } + }, + "children": + [ + { + "attributes": + { + "abstract": false, + "baseContracts": + [ + null + ], + "contractDependencies": + [ + null + ], + "contractKind": "contract", + "fullyImplemented": true, + "linearizedBaseContracts": + [ + 5 + ], + "name": "C", + "nodes": + [ + null + ], + "scope": 6 + }, + "children": + [ + { + "attributes": + { + "text": "This contract is empty\nand has a line-breaking comment." + }, + "id": 4, + "name": "StructuredDocumentation", + "src": "0:61:2" + } + ], + "id": 5, + "name": "ContractDefinition", + "src": "62:13:2" + } + ], + "id": 6, + "name": "SourceUnit", + "src": "62:14:2" +}, { "attributes": { @@ -240,3 +361,4 @@ "name": "SourceUnit", "src": "0:214:3" } +] diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 979c79aa5..76c8e50c7 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -131,9 +131,8 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi } c.setSources(sources); c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - if (c.parse()) - c.analyze(); - else + + if (!c.parse()) { SourceReferenceFormatterHuman formatter(_stream, _formatted, false); for (auto const& error: c.errors()) @@ -141,85 +140,99 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi return TestResult::FatalError; } - if (m_sources.size() > 1) - m_result += "[\n"; + c.analyze(); - for (size_t i = 0; i < m_sources.size(); i++) - { - ostringstream result; - ASTJsonConverter(false, sourceIndices).print(result, c.ast(m_sources[i].first)); - m_result += result.str(); - if (i != m_sources.size() - 1) - m_result += ","; - m_result += "\n"; - } + bool resultsMatch = runTest( + m_expectation, + m_result, + sourceIndices, + c, + false, + "", + _stream, + _linePrefix, + _formatted + ); - if (m_sources.size() > 1) - m_result += "]\n"; - - bool resultsMatch = true; - - replaceTagWithVersion(m_expectation); - - if (m_expectation != m_result) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - { - istringstream stream(m_expectation); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - { - istringstream stream(m_result); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - resultsMatch = false; - } - - for (size_t i = 0; i < m_sources.size(); i++) - { - ostringstream result; - ASTJsonConverter(true, sourceIndices).print(result, c.ast(m_sources[i].first)); - m_resultLegacy = result.str(); - if (i != m_sources.size() - 1) - m_resultLegacy += ","; - m_resultLegacy += "\n"; - } - - replaceTagWithVersion(m_expectationLegacy); - - if (m_expectationLegacy != m_resultLegacy) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result (legacy):" << endl; - { - istringstream stream(m_expectationLegacy); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result (legacy):" << endl; - { - istringstream stream(m_resultLegacy); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - resultsMatch = false; - } + resultsMatch = runTest( + m_expectationLegacy, + m_resultLegacy, + sourceIndices, + c, + true, + "legacy", + _stream, + _linePrefix, + _formatted + ) && resultsMatch; return resultsMatch ? TestResult::Success : TestResult::Failure; } +bool ASTJSONTest::runTest( + string& _expectation, + string& _result, + map const& _sourceIndices, + CompilerStack& _compiler, + bool _legacy, + string const& _variation, + ostream& _stream, + string const& _linePrefix, + bool const _formatted +) +{ + if (m_sources.size() > 1) + _result += "[\n"; + + for (size_t i = 0; i < m_sources.size(); i++) + { + ostringstream result; + ASTJsonConverter(_legacy, _sourceIndices).print(result, _compiler.ast(m_sources[i].first)); + _result += result.str(); + if (i != m_sources.size() - 1) + _result += ","; + _result += "\n"; + } + + if (m_sources.size() > 1) + _result += "]\n"; + + replaceTagWithVersion(_expectation); + + if (_expectation != _result) + { + string nextIndentLevel = _linePrefix + " "; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << + _linePrefix << + "Expected result" << + (!_variation.empty() ? " (" + _variation + "):" : ":") << + endl; + { + istringstream stream(_expectation); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << + _linePrefix << + "Obtained result" << + (!_variation.empty() ? " (" + _variation + "):" : ":") << + endl; + { + istringstream stream(_result); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + return false; + } + + return true;; +} + void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const { for (auto const& source: m_sources) @@ -236,24 +249,20 @@ void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) const { - ofstream file(m_astFilename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write AST expectation to \"" + m_astFilename + "\".")); + updateExpectation(m_astFilename, m_result, ""); + updateExpectation(m_legacyAstFilename, m_resultLegacy, "legacy "); +} + +void ASTJSONTest::updateExpectation(string const& _filename, string const& _expectation, string const& _variation) const +{ + ofstream file(_filename.c_str()); + if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write " + _variation + "AST expectation to \"" + _filename + "\".")); file.exceptions(ios::badbit); - string replacedResult = m_result; + string replacedResult = _expectation; replaceVersionWithTag(replacedResult); file << replacedResult; file.flush(); file.close(); - - file.open(m_legacyAstFilename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write legacy AST expectation to \"" + m_legacyAstFilename + "\".")); - - string replacedResultLegacy = m_resultLegacy; - replaceVersionWithTag(replacedResultLegacy); - - file << replacedResultLegacy; - file.flush(); - file.close(); } diff --git a/test/libsolidity/ASTJSONTest.h b/test/libsolidity/ASTJSONTest.h index 4e74d79f3..5a5caac05 100644 --- a/test/libsolidity/ASTJSONTest.h +++ b/test/libsolidity/ASTJSONTest.h @@ -26,9 +26,15 @@ #include #include +namespace solidity::frontend +{ +class CompilerStack; +} + namespace solidity::frontend::test { + class ASTJSONTest: public TestCase { public: @@ -41,6 +47,23 @@ public: void printSource(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; private: + bool runTest( + std::string& _expectation, + std::string& _result, + std::map const& _sourceIndicies, + CompilerStack& _compiler, + bool _legacy, + std::string const& _variation, + std::ostream& _stream, + std::string const& _linePrefix = "", + bool const _formatted = false + ); + void updateExpectation( + std::string const& _filename, + std::string const& _expectation, + std::string const& _variation + ) const; + std::vector> m_sources; std::string m_expectationLegacy; std::string m_astFilename; diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 6ad68d85b..53c93c726 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -511,22 +511,6 @@ BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) -{ - char const* text = R"( - contract c { - uint private internal a; - } - )"; - CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); - text = R"( - contract c { - function f() private external {} - } - )"; - CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); -} - BOOST_AUTO_TEST_CASE(keyword_is_reserved) { auto keywords = { diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index b837b3878..49e6f6ac4 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(helper_bool_result) BoolResult r7{true}; // Attention: this will implicitly convert to bool. - BoolResult r8{"true"}; + BoolResult r8("true"); // We cannot use {} initializer here because this does not allow narrowing conversion (at least MSVC breaks) r7.merge(r8, logical_and()); BOOST_REQUIRE_EQUAL(r7.get(), true); BOOST_REQUIRE_EQUAL(r7.message(), ""); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 570350466..d71cf10f1 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -428,9 +428,9 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject()); BOOST_CHECK_EQUAL( util::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]), - "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]},\"license\":null},\"children\":" + "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":" "[{\"attributes\":{\"abstract\":false,\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\"," - "\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," + "\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" ); } diff --git a/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol b/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol new file mode 100644 index 000000000..67c4cf7b4 --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.0.0; + +contract Error7 { + constructor() { + a = +// ---- +// ParserError 6933: (76-76): Expected primary expression. +// ParserError 1957: (76-76): In Statement, ';'is expected; got end of source instead. +// ParserError 1957: (76-76): In Block, '}'is expected; got end of source instead. +// ParserError 1957: (76-76): In ContractDefinition, '}'is expected; got end of source instead. diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index b1bc1ff5d..74cd89a42 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 603000 -// executionCost: 638 -// totalCost: 603638 +// codeDepositCost: 616600 +// executionCost: 651 +// totalCost: 617251 // external: // a(): 1029 // b(uint256): 2084 diff --git a/test/libsolidity/semanticTests/exponentiation/signed_base.sol b/test/libsolidity/semanticTests/exponentiation/signed_base.sol index f58c08129..045d322d0 100644 --- a/test/libsolidity/semanticTests/exponentiation/signed_base.sol +++ b/test/libsolidity/semanticTests/exponentiation/signed_base.sol @@ -10,5 +10,7 @@ contract test { return (x**y1, x**y2); } } +// ==== +// compileViaYul: also // ---- // f() -> 9, -27 diff --git a/test/libsolidity/semanticTests/freeFunctions/easy.sol b/test/libsolidity/semanticTests/freeFunctions/easy.sol new file mode 100644 index 000000000..04b023d52 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/easy.sol @@ -0,0 +1,13 @@ +function add(uint a, uint b) pure returns (uint) { + return a + b; +} + +contract C { + function f(uint x) public pure returns (uint) { + return add(x, 2); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 7 -> 9 diff --git a/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol new file mode 100644 index 000000000..7a05ff5d0 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol @@ -0,0 +1,15 @@ +contract C { + uint public x = 2; +} + +function test() returns (bool) { + return type(C).runtimeCode.length > 20; +} + +contract D { + function f() public returns (bool) { + return test(); + } +} +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/freeFunctions/import.sol b/test/libsolidity/semanticTests/freeFunctions/import.sol new file mode 100644 index 000000000..edd44de49 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/import.sol @@ -0,0 +1,18 @@ +==== Source: A ==== +struct S { uint x; } +function set(S storage a, uint v) { a.x = v; } + +==== Source: B ==== +import "A"; +import "A" as A; +contract C { + A.S data; + function f(uint v) public returns (uint one, uint two) { + A.set(data, v); + one = data.x; + set(data, v + 1); + two = data.x; + } +} +// ---- +// f(uint256): 7 -> 7, 8 diff --git a/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol new file mode 100644 index 000000000..3632f6a6a --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol @@ -0,0 +1,21 @@ +library L { + function pub() public pure returns (uint) { + return 7; + } + function inter() internal pure returns (uint) { + return 8; + } +} + +function fu() pure returns (uint, uint) { + return (L.pub(), L.inter()); +} + +contract C { + function f() public pure returns (uint, uint) { + return fu(); + } +} +// ---- +// library: L +// f() -> 7, 8 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/freeFunctions/new_operator.sol b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol new file mode 100644 index 000000000..e35f1c9ea --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol @@ -0,0 +1,15 @@ +contract C { + uint public x = 2; +} + +function test() returns (uint) { + return (new C()).x(); +} + +contract D { + function f() public returns (uint) { + return test(); + } +} +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/freeFunctions/overloads.sol b/test/libsolidity/semanticTests/freeFunctions/overloads.sol new file mode 100644 index 000000000..9b2a914eb --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/overloads.sol @@ -0,0 +1,16 @@ +function f(uint) returns (uint) { + return 2; +} +function f(string memory) returns (uint) { + return 3; +} + +contract C { + function g() public returns (uint, uint) { + return (f(2), f("abc")); + } +} +// ==== +// compileViaYul: also +// ---- +// g() -> 2, 3 diff --git a/test/libsolidity/semanticTests/freeFunctions/recursion.sol b/test/libsolidity/semanticTests/freeFunctions/recursion.sol new file mode 100644 index 000000000..fd21fe097 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/recursion.sol @@ -0,0 +1,23 @@ +function exp(uint base, uint exponent) pure returns (uint power) { + if (exponent == 0) + return 1; + power = exp(base, exponent / 2); + power *= power; + if (exponent & 1 == 1) + power *= base; +} + +contract C { + function g(uint base, uint exponent) public pure returns (uint) { + return exp(base, exponent); + } +} +// ==== +// compileViaYul: also +// ---- +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 10 -> 59049 +// g(uint256,uint256): 2, 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol new file mode 100644 index 000000000..3b94b06fc --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol @@ -0,0 +1,17 @@ +contract C { + uint[] data; + function f(uint x, uint[] calldata input) public returns (uint, uint) { + data.push(x); + (uint a, uint[] calldata b) = fun(input, data); + return (a, b[1]); + + } +} + +function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256[]): 7, 0x40, 3, 8, 9, 10 -> 7, 9 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol index 3d31c1add..63e9f0a2d 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol @@ -3,9 +3,9 @@ contract C { assembly { let d:= 0x10 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := g(a) - function g(r) - > s { + function g(r) -> s { s := mul(r, r) } y := g(b) diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol index 17a271d5b..386db6244 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol @@ -1,7 +1,7 @@ contract C { function f() public { assembly { - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol index 26d3d43b7..1cbc510b9 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol @@ -3,7 +3,7 @@ contract C { assembly { let d := 0x10 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol index 2dec9761b..77d28fd4b 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol @@ -3,7 +3,7 @@ contract C { assembly { let a1, b1, c1 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol new file mode 100644 index 000000000..03f334658 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol @@ -0,0 +1,18 @@ +contract C { + uint256[] public a; + + function f() public returns (uint256) { + uint256[] storage x = a; + uint256 off; + assembly { + sstore(x.slot, 7) + off := x.offset + } + assert(off == 0); + return a.length; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol b/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol new file mode 100644 index 000000000..069e46532 --- /dev/null +++ b/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol @@ -0,0 +1,29 @@ +abstract contract A { + function f() virtual public pure; +} + +interface I { + function f() external pure; +} + +contract C { + function f() pure public { + } +} + +contract Test { + function c() public pure returns (string memory) { + return type(C).name; + } + function a() public pure returns (string memory) { + return type(A).name; + } + function i() public pure returns (string memory) { + return type(I).name; + } +} + +// ---- +// c() -> 0x20, 1, "C" +// a() -> 0x20, 1, "A" +// i() -> 0x20, 1, "I" diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol index db8171a20..04d675371 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol @@ -1,6 +1,5 @@ pragma experimental ABIEncoderV2; - contract C { struct S { uint256 a; @@ -13,5 +12,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol index e0a25a857..19b2f64a7 100644 --- a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol @@ -23,5 +23,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/viaYul/exp.sol b/test/libsolidity/semanticTests/viaYul/exp.sol new file mode 100644 index 000000000..aa8318d08 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256): 0, 0 -> 1 +// f(uint256,uint256): 0, 1 -> 0x00 +// f(uint256,uint256): 0, 2 -> 0x00 +// f(uint256,uint256): 1, 0 -> 1 +// f(uint256,uint256): 1, 1 -> 1 +// f(uint256,uint256): 1, 2 -> 1 +// f(uint256,uint256): 2, 0 -> 1 +// f(uint256,uint256): 2, 1 -> 2 +// f(uint256,uint256): 2, 2 -> 4 +// f(uint256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343 +// f(uint256,uint256): 128, 2 -> 0x4000 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/viaYul/exp_neg.sol b/test/libsolidity/semanticTests/viaYul/exp_neg.sol new file mode 100644 index 000000000..aa2f5eb18 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_neg.sol @@ -0,0 +1,28 @@ +contract C { + function f(int x, uint y) public returns (int) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(int256,uint256): 0, 0 -> 1 +// f(int256,uint256): 0, 1 -> 0x00 +// f(int256,uint256): 0, 2 -> 0x00 +// f(int256,uint256): 1, 0 -> 1 +// f(int256,uint256): 1, 1 -> 1 +// f(int256,uint256): 1, 2 -> 1 +// f(int256,uint256): 2, 0 -> 1 +// f(int256,uint256): 2, 1 -> 2 +// f(int256,uint256): 2, 2 -> 4 +// f(int256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343 +// f(int256,uint256): 128, 2 -> 0x4000 +// f(int256,uint256): -1, 0 -> 1 +// f(int256,uint256): -1, 1 -> -1 +// f(int256,uint256): -1, 2 -> 1 +// f(int256,uint256): -2, 0 -> 1 +// f(int256,uint256): -2, 1 -> -2 +// f(int256,uint256): -2, 2 -> 4 +// f(int256,uint256): -7, 63 -> -174251498233690814305510551794710260107945042018748343 +// f(int256,uint256): -128, 2 -> 0x4000 +// f(int256,uint256): -1, 115792089237316195423570985008687907853269984665640564039457584007913129639935 -> -1 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol b/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol new file mode 100644 index 000000000..a55cbebaa --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol @@ -0,0 +1,38 @@ +contract C { + function f(int8 x, uint y) public returns (int) { + return x**y; + } + function g(int256 x, uint y) public returns (int) { + return x**y; + } +} +// ==== +// compileViaYul: true +// ---- +// f(int8,uint256): 2, 6 -> 64 +// f(int8,uint256): 2, 7 -> FAILURE +// f(int8,uint256): 2, 8 -> FAILURE +// f(int8,uint256): -2, 6 -> 64 +// f(int8,uint256): -2, 7 -> -128 +// f(int8,uint256): -2, 8 -> FAILURE +// f(int8,uint256): 6, 3 -> FAILURE +// f(int8,uint256): 7, 2 -> 0x31 +// f(int8,uint256): 7, 3 -> FAILURE +// f(int8,uint256): -7, 2 -> 0x31 +// f(int8,uint256): -7, 3 -> FAILURE +// f(int8,uint256): -7, 4 -> FAILURE +// f(int8,uint256): 127, 31 -> FAILURE +// f(int8,uint256): 127, 131 -> FAILURE +// f(int8,uint256): -128, 0 -> 1 +// f(int8,uint256): -128, 1 -> -128 +// f(int8,uint256): -128, 31 -> FAILURE +// f(int8,uint256): -128, 131 -> FAILURE +// f(int8,uint256): -11, 2 -> 121 +// f(int8,uint256): -12, 2 -> FAILURE +// f(int8,uint256): 12, 2 -> FAILURE +// f(int8,uint256): -5, 3 -> -125 +// f(int8,uint256): -6, 3 -> FAILURE +// g(int256,uint256): -7, 90 -> 11450477594321044359340126713545146077054004823284978858214566372120240027249 +// g(int256,uint256): -7, 91 -> FAILURE +// g(int256,uint256): -63, 42 -> 3735107253208426854890677539053540390278853997836851167913009474475553834369 +// g(int256,uint256): -63, 43 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/exp_overflow.sol b/test/libsolidity/semanticTests/viaYul/exp_overflow.sol new file mode 100644 index 000000000..c23b61a86 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_overflow.sol @@ -0,0 +1,31 @@ +contract C { + function f(uint8 x, uint8 y) public returns (uint) { + return x**y; + } + function g(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: true +// ---- +// f(uint8,uint8): 2, 7 -> 0x80 +// f(uint8,uint8): 2, 8 -> FAILURE +// f(uint8,uint8): 15, 2 -> 225 +// f(uint8,uint8): 6, 3 -> 0xd8 +// f(uint8,uint8): 7, 2 -> 0x31 +// f(uint8,uint8): 7, 3 -> FAILURE +// f(uint8,uint8): 7, 4 -> FAILURE +// f(uint8,uint8): 255, 31 -> FAILURE +// f(uint8,uint8): 255, 131 -> FAILURE +// g(uint256,uint256): 0x200000000000000000000000000000000, 1 -> 0x0200000000000000000000000000000000 +// g(uint256,uint256): 0x100000000000000000000000000000010, 2 -> FAILURE +// g(uint256,uint256): 0x200000000000000000000000000000000, 2 -> FAILURE +// g(uint256,uint256): 0x200000000000000000000000000000000, 3 -> FAILURE +// g(uint256,uint256): 255, 31 -> 400631961586894742455537928461950192806830589109049416147172451019287109375 +// g(uint256,uint256): 255, 32 -> -13630939032658036097408813250890608687528184442832962921928608997994916749311 +// g(uint256,uint256): 255, 33 -> FAILURE +// g(uint256,uint256): 255, 131 -> FAILURE +// g(uint256,uint256): 258, 31 -> 575719427506838823084316385994930914701079543089399988096291424922125729792 +// g(uint256,uint256): 258, 37 -> FAILURE +// g(uint256,uint256): 258, 131 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/exp_various.sol b/test/libsolidity/semanticTests/viaYul/exp_various.sol new file mode 100644 index 000000000..7d7a166d9 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_various.sol @@ -0,0 +1,50 @@ +contract C { + function f(uint8 x, uint8 y) public returns (uint) { + return x**y; + } + function g(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint8,uint8): 0, 0 -> 1 +// f(uint8,uint8): 0, 1 -> 0x00 +// f(uint8,uint8): 0, 2 -> 0x00 +// f(uint8,uint8): 0, 3 -> 0x00 +// f(uint8,uint8): 1, 0 -> 1 +// f(uint8,uint8): 1, 1 -> 1 +// f(uint8,uint8): 1, 2 -> 1 +// f(uint8,uint8): 1, 3 -> 1 +// f(uint8,uint8): 2, 0 -> 1 +// f(uint8,uint8): 2, 1 -> 2 +// f(uint8,uint8): 2, 2 -> 4 +// f(uint8,uint8): 2, 3 -> 8 +// f(uint8,uint8): 3, 0 -> 1 +// f(uint8,uint8): 3, 1 -> 3 +// f(uint8,uint8): 3, 2 -> 9 +// f(uint8,uint8): 3, 3 -> 0x1b +// f(uint8,uint8): 10, 0 -> 1 +// f(uint8,uint8): 10, 1 -> 0x0a +// f(uint8,uint8): 10, 2 -> 100 +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 0, 2 -> 0x00 +// g(uint256,uint256): 0, 3 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 1, 1 -> 1 +// g(uint256,uint256): 1, 2 -> 1 +// g(uint256,uint256): 1, 3 -> 1 +// g(uint256,uint256): 2, 0 -> 1 +// g(uint256,uint256): 2, 1 -> 2 +// g(uint256,uint256): 2, 2 -> 4 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 0 -> 1 +// g(uint256,uint256): 3, 1 -> 3 +// g(uint256,uint256): 3, 2 -> 9 +// g(uint256,uint256): 3, 3 -> 0x1b +// g(uint256,uint256): 10, 10 -> 10000000000 +// g(uint256,uint256): 10, 77 -> -15792089237316195423570985008687907853269984665640564039457584007913129639936 +// g(uint256,uint256): 256, 2 -> 0x010000 +// g(uint256,uint256): 256, 31 -> 0x0100000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol new file mode 100644 index 000000000..28d671780 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.push(); + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol new file mode 100644 index 000000000..5567afa87 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.length; + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol new file mode 100644 index 000000000..0cbb15ea0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.pop(); + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here +// Warning 2529: (93-100): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol new file mode 100644 index 000000000..241d02c79 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.length; + a.pop(); + } +} +// ---- +// Warning 2529: (94-101): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol new file mode 100644 index 000000000..6a05d3af8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function g() internal { + a.push(); + } + function f() public { + a.pop(); + g(); + } +} +// ---- +// Warning 2529: (122-129): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol new file mode 100644 index 000000000..40e081d30 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function g() internal view { + a.length; + } + function f() public { + a.pop(); + g(); + } +} +// ---- +// Warning 2529: (127-134): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol new file mode 100644 index 000000000..34663d931 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.push(); + a.pop(); + } +} diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol new file mode 100644 index 000000000..517755aca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.push(); + a.push(); + a.push(); + a.pop(); + a.pop(); + a.pop(); + } +} +// ---- +// Warning 2529: (82-89): Empty array "pop" detected here diff --git a/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol index c409a0e09..866cfe1eb 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol @@ -10,5 +10,5 @@ contract C { } } // ---- +// Warning 3944: (162-177): Underflow (resulting value less than 0) happens here // Warning 6328: (150-184): Assertion violation happens here -// Warning 4144: (162-177): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol index 8192f673d..5fa9ef7d9 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol @@ -12,5 +12,5 @@ contract C { } } // ---- +// Warning 3944: (217-232): Underflow (resulting value less than 0) happens here // Warning 6328: (205-239): Assertion violation happens here -// Warning 4144: (217-232): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol index 44808ce4c..4e92b34eb 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol @@ -53,5 +53,4 @@ contract MyConc{ // ---- // Warning 2519: (773-792): This declaration shadows an existing declaration. // Warning 2018: (1009-1086): Function state mutability can be restricted to view -// Warning 6084: (985-1002): Underflow (resulting value less than 0) happens here. -// Warning 6084: (985-1002): Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (985-1002): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol index e969e3c3d..66f262787 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol index 186a2e1a8..650bce10a 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (227-236): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here 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 1dd387cc9..77e2f247f 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 @@ -19,4 +19,3 @@ contract c { // ---- // Warning 6328: (202-218): Assertion violation happens here // Warning 6328: (242-252): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol index ff526db60..905964ed5 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol index 32fbccdd7..bc9c800af 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol index d6007e676..1a6cb909a 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol index edada1444..6640e399c 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol index eb54e2236..77ec37ebd 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol @@ -25,4 +25,3 @@ contract c { } // ---- // Warning 6328: (360-370): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol index c585abdb4..c4a823dbc 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol index 1a055a9d7..6615651fd 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol @@ -16,4 +16,3 @@ contract c { } // ---- // Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index b50177619..789b98368 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -19,6 +19,6 @@ contract A is B { } } // ---- +// Warning 4984: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (244-249): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (232-250): Assertion violation happens here -// Warning 2661: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (244-249): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol index c2466c0d1..6e8c4fe95 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol @@ -18,6 +18,6 @@ contract A is B { } } // ---- -// Warning 2661: (207-212): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (198-203): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (198-203): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (207-212): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol index 29c2d41b2..e5c8d5b38 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -25,7 +25,6 @@ contract A is B2, B1 { } } // ---- +// Warning 4984: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (314-319): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (302-320): Assertion violation happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (314-319): Overflow (resulting value larger than 2**256 - 1) 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 83cb5dc64..b09b836ec 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -25,7 +25,6 @@ contract A is B2, B1 { } } // ---- +// Warning 4984: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (314-319): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (302-320): Assertion violation happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (314-319): Overflow (resulting value larger than 2**256 - 1) 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 cdd479268..f6bfc6d94 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -27,11 +27,7 @@ contract A is B2, B1 { } } // ---- +// Warning 4984: (160-165): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (334-350): Assertion violation happens here -// Warning 4144: (160-165): Underflow (resulting value less than 0) happens here -// Warning 2661: (160-165): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (160-165): Overflow (resulting value larger than 2**256 - 1) 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 223703be4..92e53f40c 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 @@ -25,6 +25,5 @@ contract A is B { } } // ---- +// Warning 4984: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (328-342): Assertion violation happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol index 106e60440..6771f6a05 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol @@ -13,5 +13,5 @@ contract C { } } // ---- +// Warning 4984: (115-120): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (162-176): Assertion violation happens here -// Warning 2661: (115-120): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol index 3439ec6d4..ef7cb6647 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol @@ -7,6 +7,3 @@ contract c { bool b = (f() > 0) || (f() > 0); } // ---- -// Warning 2661: (100-105): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (100-105): Underflow (resulting value less than 0) happens here -// Warning 2661: (100-105): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/library_constant.sol b/test/libsolidity/smtCheckerTests/functions/library_constant.sol index ee521a181..2a72c5216 100644 --- a/test/libsolidity/smtCheckerTests/functions/library_constant.sol +++ b/test/libsolidity/smtCheckerTests/functions/library_constant.sol @@ -20,7 +20,6 @@ contract C { } // ---- // Warning 6328: (136-155): Assertion violation happens here -// Warning 2661: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (327-332): Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 8364: (300-302): Assertion checker does not yet implement type type(library l1) -// Warning 2661: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (327-332): Overflow (resulting value larger than 2**256 - 1) happens here 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 223703be4..92e53f40c 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 @@ -25,6 +25,5 @@ contract A is B { } } // ---- +// Warning 4984: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (328-342): Assertion violation happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) 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 a9323fdbc..5538b9ee8 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 @@ -22,12 +22,10 @@ contract A is B { } // ---- +// Warning 4984: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (216-221): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (261-270): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (287-292): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (275-293): Assertion violation happens here -// Warning 4144: (157-162): Underflow (resulting value less than 0) happens here -// Warning 2661: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (216-221): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (261-270): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (287-292): Overflow (resulting value larger than 2**256 - 1) 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 c0ad1b083..f597d60e4 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 @@ -22,10 +22,9 @@ contract A is B { } // ---- +// Warning 4984: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (240-245): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (262-268): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (285-290): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (273-291): Assertion violation happens here -// Warning 2661: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (240-245): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (262-268): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (285-290): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol index b4d819887..c6cbafaef 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol @@ -5,7 +5,6 @@ contract C function f(uint x) public pure { require(x < 100); do { - // Overflows due to resetting x. x = x + 1; } while (x < 10); assert(x < 14); @@ -14,5 +13,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (179-193): Assertion violation happens here -// Warning 2661: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (143-157): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol index 55f7975f0..2345cc4f3 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol @@ -5,15 +5,10 @@ contract C function f(uint x) public pure { require(x < 100); do { - // Overflows due to resetting x. x = x + 1; } while (x < 1000); - // The assertion is true but we can't infer so - // because x is touched in the loop. assert(x > 0); } } // ==== // SMTSolvers: z3 -// ---- -// Warning 2661: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol index de7fdf973..0579325df 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol @@ -14,5 +14,6 @@ contract C // ==== // SMTSolvers: z3 // ---- +// Warning 1218: (176-181): Error trying to invoke SMT solver. // Warning 6328: (189-203): Assertion violation happens here // Warning 2661: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol index ba6a6a8d2..d10ba5b42 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol @@ -19,6 +19,7 @@ contract LoopFor2 { // ==== // SMTSolvers: z3 // ---- +// Warning 1218: (244-249): Error trying to invoke SMT solver. // Warning 6328: (281-301): Assertion violation happens here // Warning 6328: (305-324): Assertion violation happens here // Warning 6328: (328-347): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol index b977ed326..b61baaac9 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol @@ -23,5 +23,6 @@ contract LoopFor2 { // ==== // SMTSolvers: z3 // ---- +// Warning 1218: (237-242): Error trying to invoke SMT solver. // Warning 6328: (362-382): Assertion violation happens here // Warning 6328: (409-428): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol index de397d4d3..c26a19a1a 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol @@ -20,6 +20,9 @@ contract LoopFor2 { assert(b[0] == 900); } } +// ==== +// SMTSolvers: cvc4 // ---- -// Warning 6328: (320-339): Assertion violation happens here -// Warning 6328: (343-362): Assertion violation happens here. +// Warning 4661: (296-316): Assertion violation happens here +// Warning 4661: (320-339): Assertion violation happens here +// Warning 4661: (343-362): 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 5a2dbbcba..f543be8db 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,5 +21,5 @@ contract C } } // ---- +// Warning 4984: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (136-149): Assertion violation happens here -// Warning 2661: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol index 2b5f131d7..52dd6ee5f 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol @@ -15,4 +15,3 @@ contract C } } // ---- -// Warning 2661: (145-150): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol new file mode 100644 index 000000000..dc1d8f206 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 0xff; + uint8 y = ~x; + assert(x & y == 0); + assert(x | y == 0xff); + assert(x ^ y == 0xff); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol new file mode 100644 index 000000000..1431fb248 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (byte) { + return (~byte(0xFF)); + } +} +// ---- +// Warning 5084: (102-112): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol new file mode 100644 index 000000000..d879f8ee7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int16 x = 1; + assert(~x == 0); + x = 0xff; + assert(~x == 0); + x = 0x0f; + assert(~x == 0xf0); + x = -1; + assert(~x != 0); + x = -2; + assert(~x == 1); + } +} +// ---- +// Warning 6328: (91-106): Assertion violation happens here +// Warning 6328: (122-137): Assertion violation happens here +// Warning 6328: (153-171): Assertion violation happens here +// Warning 6328: (185-200): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol new file mode 100644 index 000000000..8b733de18 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 0xff; + assert(~x == 0x00); + uint16 y = 0xff00; + assert(~y == 0xff); + assert(~y == 0xffff); + assert(~y == 0x0000); + } +} +// ---- +// Warning 6328: (159-179): Assertion violation happens here +// Warning 6328: (183-203): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol new file mode 100644 index 000000000..4dad0286e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (byte) { + return (byte(0x0F) | (byte(0xF0))); + } +} +// ---- +// Warning 5084: (101-111): Type conversion is not yet fully supported and might yield false positives. +// Warning 5084: (115-125): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol new file mode 100644 index 000000000..140c255c6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int16 x = 1; + int16 y = 0; + assert(x | y == 1); + x = 0; y = 0; + assert(x | y != 0); + y = 240; + x = 15; + int16 z = x | y; + assert(z == 255); + x = -1; y = 200; + assert(x | y == x); + assert(x | z != -1); + } +} +// ---- +// Warning 6328: (144-162): Assertion violation happens here +// Warning 6328: (267-286): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol new file mode 100644 index 000000000..f93c59a69 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 1; + uint16 y = 0; + assert(x | y != 0); + x = 0xff; + y = 0xff00; + assert(x | y == 0xff); + assert(x | y == 0xffff); + assert(x | y == 0x0000); + } +} +// ---- +// Warning 6328: (155-176): Assertion violation happens here +// Warning 6328: (207-230): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol index 3852563be..69cea474f 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol @@ -6,4 +6,3 @@ contract Simp { } } // ---- -// Warning 1093: (142-152): Assertion checker does not yet implement this bitwise operator. diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol new file mode 100644 index 000000000..87bdd7f8d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int8 x = 1; + int16 y = 0; + assert(x ^ y == 1); + int16 z = -1; + assert(x ^ z == -2); + assert(y ^ z == -1); + assert(y ^ z > 0); + x = 7; y = 3; + assert(x ^ y < 5); + assert(x ^ y > 5); + } +} +// ---- +// Warning 6328: (189-206): Assertion violation happens here +// Warning 6328: (247-264): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol new file mode 100644 index 000000000..70ae0eac6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 1; + uint16 y = 0; + assert(x ^ y != 0); + x = 0xff; + y = 0xff00; + assert(x ^ y == 0xff); + assert(x ^ y == 0xffff); + assert(x ^ y == 0x0000); + } +} +// ---- +// Warning 6328: (155-176): Assertion violation happens here +// Warning 6328: (207-230): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol new file mode 100644 index 000000000..f01fb6029 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b) public pure { + uint a = b ? 2 : 3; + assert(a > 2); + } +} +// ---- +// Warning 6328: (104-117): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol new file mode 100644 index 000000000..99ee3c2a0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint b) public pure { + require(b < 3); + uint c = (b > 0) ? b++ : ++b; + assert(c == 0); + } +} +// ---- +// Warning 6328: (132-146): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol new file mode 100644 index 000000000..12c580e24 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a, uint b) public pure { + require(a < 10); + require(b <= a); + + uint c = (b > 4) ? a++ : b++; + assert(c > a); + } +} +// ---- +// Warning 6328: (161-174): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol new file mode 100644 index 000000000..87b48e982 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() public { + x = 3; + } +} +// ---- +// Warning 2072: (273-279): Unused local variable. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol new file mode 100644 index 000000000..675ae3eb5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + d.d(); + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() public { + x = 3; + } +} +// ---- +// Warning 2072: (288-294): Unused local variable. +// Warning 6328: (318-342): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol new file mode 100644 index 000000000..d1fe62aac --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + d.d(); + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() internal { + x = 3; + } +} +// ---- +// Warning 2072: (288-294): Unused local variable. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol new file mode 100644 index 000000000..7958e0237 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint b) public pure returns (uint d) { + require(b < 10); + uint c = b < 5 ? 5 : 1; + d = c > 5 ? 3 : 2; + } +} +// ---- +// Warning 6838: (148-153): Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol new file mode 100644 index 000000000..705ca9c2b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b) public pure { + require(b); + uint c = b ? 5 : 1; + assert(c < 5); + } +} +// ---- +// Warning 6328: (118-131): Assertion violation happens here +// Warning 6838: (105-106): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol new file mode 100644 index 000000000..890db61ca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) internal pure returns (bool b) { + b = a > 5; + } + function g(uint a) public pure { + uint c = f(a) ? 3 : 4; + assert(c > 5); + } +} +// ---- +// Warning 6328: (203-216): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol new file mode 100644 index 000000000..47ec79ab5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) internal pure returns (uint b) { + require(a < 1000); + return a * a; + } + function g(uint a) internal pure returns (uint b) { + require(a < 1000); + return a + 100; + } + function h(uint a) public pure { + uint c = a < 5 ? g(a) : f(a); + assert(c >= 25); + assert(c < 20); + } +} +// ---- +// Warning 6328: (378-392): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol new file mode 100644 index 000000000..cd85df0c4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b1, bool b2) public pure { + require(b1 || b2); + uint c = b1 ? 3 : (b2 ? 2 : 1); + assert(c > 1); + } +} +// ---- +// Warning 6838: (147-149): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol new file mode 100644 index 000000000..0dabdf0f6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b1, bool b2) public pure { + uint c = b1 ? 3 : (b2 ? 2 : 1); + assert(c > 1); + } +} +// ---- +// Warning 6328: (141-154): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol new file mode 100644 index 000000000..3c402fceb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + uint a; + bool b; + + function f() public returns(uint c) { + c = b ? a + 1 : a--; + assert(c > a); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/div_zero.sol b/test/libsolidity/smtCheckerTests/operators/div_zero.sol new file mode 100644 index 000000000..81f32822b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/div_zero.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + uint z = 0; + uint x = 2 / z; +} +// ---- +// Warning 6084: (69-74): Division by zero happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/division_3.sol b/test/libsolidity/smtCheckerTests/operators/division_3.sol index 03af864ef..dd82c5bb7 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_3.sol @@ -6,4 +6,5 @@ contract C { } } // ---- +// Warning 1218: (127-132): Error trying to invoke SMT solver. // Warning 2661: (127-132): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/division_4.sol b/test/libsolidity/smtCheckerTests/operators/division_4.sol index ce67e3375..52e3db08b 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_4.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_4.sol @@ -6,3 +6,5 @@ contract C { return x / y; } } +// ---- +// Warning 1218: (147-152): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_5.sol b/test/libsolidity/smtCheckerTests/operators/division_5.sol index ed6966223..6458dd8e3 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_5.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_5.sol @@ -9,3 +9,5 @@ contract C { return c; } } +// ---- +// Warning 1218: (151-156): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_6.sol b/test/libsolidity/smtCheckerTests/operators/division_6.sol index 24e7a6325..7da33cbaa 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_6.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_6.sol @@ -11,3 +11,5 @@ contract C { return c; } } +// ---- +// Warning 1218: (265-270): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol index 6473b98f3..a5a8daf59 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (112-117): Error trying to invoke SMT solver. // Warning 1218: (105-123): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol index c9a932a42..8c903a242 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (113-118): Error trying to invoke SMT solver. // Warning 1218: (106-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol index 95743c934..0514b894f 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (113-118): Error trying to invoke SMT solver. // Warning 1218: (106-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol index 2570b375f..348ad0dd5 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol @@ -7,4 +7,5 @@ contract C { } } // ---- +// Warning 1218: (114-119): Error trying to invoke SMT solver. // Warning 1218: (107-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol index c92a1ac5d..e8e388c15 100644 --- a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol @@ -6,7 +6,6 @@ contract test { } // ---- // Warning 2072: (80-88): Unused local variable. +// Warning 4984: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 5084: (91-100): Type conversion is not yet fully supported and might yield false positives. // Warning 5084: (103-112): Type conversion is not yet fully supported and might yield false positives. -// Warning 4144: (91-112): Underflow (resulting value less than 0) happens here -// Warning 2661: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol index b815b0c94..8a8af3a5c 100644 --- a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol @@ -4,5 +4,3 @@ contract C { function f() internal { b[0] += 1; } } // ---- -// Warning 4144: (84-93): Underflow (resulting value less than 0) happens here -// Warning 2661: (84-93): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/mod_even.sol b/test/libsolidity/smtCheckerTests/operators/mod_even.sol index ab88a7ede..9e8a9cd96 100644 --- a/test/libsolidity/smtCheckerTests/operators/mod_even.sol +++ b/test/libsolidity/smtCheckerTests/operators/mod_even.sol @@ -9,4 +9,5 @@ contract C } } // ---- +// Warning 1218: (113-118): Error trying to invoke SMT solver. // Warning 1218: (122-142): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol new file mode 100644 index 000000000..69411e3c2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++(x); + if (b) --(x); + if (b) delete(b); + assert(x == 0); + assert(!b); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol new file mode 100644 index 000000000..c16f01402 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++((((((x)))))); + if (b) --((((((x)))))); + if (b) delete((((((b)))))); + assert(x == 0); + assert(!b); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol new file mode 100644 index 000000000..6ab61f0ee --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++(x); + else x += 1; + assert(x == 1); + assert(!b); + } +} +// ---- +// Warning 6328: (140-150): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol index 86697dea0..05b15987f 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol @@ -13,5 +13,5 @@ contract C } } // ---- -// Warning 2661: (120-125): Overflow (resulting value larger than 255) happens here -// Warning 2661: (163-168): Overflow (resulting value larger than 255) happens here +// Warning 4984: (120-125): Overflow (resulting value larger than 255) happens here +// Warning 4984: (163-168): Overflow (resulting value larger than 255) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol index 7663fa34e..7a71a7d2f 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 2661: (117-122): Overflow (resulting value larger than 127) happens here -// Warning 2661: (150-157): Overflow (resulting value larger than 127) happens here +// Warning 4984: (117-122): Overflow (resulting value larger than 127) happens here +// Warning 4984: (150-157): Overflow (resulting value larger than 127) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol index 3eb3fd13f..c94b09b97 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol @@ -14,5 +14,5 @@ contract C } } // ---- -// Warning 2661: (154-159): Overflow (resulting value larger than 255) happens here -// Warning 2661: (185-192): Overflow (resulting value larger than 255) happens here +// Warning 4984: (154-159): Overflow (resulting value larger than 255) happens here +// Warning 4984: (185-192): Overflow (resulting value larger than 255) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol index 7e9938c74..8fc6b4997 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol @@ -14,6 +14,6 @@ contract C } } // ---- -// Warning 2661: (117-122): Overflow (resulting value larger than 127) happens here -// Warning 2661: (151-158): Overflow (resulting value larger than 127) happens here -// Warning 4144: (197-205): Underflow (resulting value less than -128) happens here +// Warning 4984: (117-122): Overflow (resulting value larger than 127) happens here +// Warning 4984: (151-158): Overflow (resulting value larger than 127) happens here +// Warning 3944: (197-205): Underflow (resulting value less than -128) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol new file mode 100644 index 000000000..fb14d7694 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x / y; + } +} +// ---- +// Warning 1218: (110-115): Error trying to invoke SMT solver. +// Warning 3046: (110-115): Division by zero happens here +// Warning 2661: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol new file mode 100644 index 000000000..a27c709cd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + require(x >= y); + return x - y; + } +} +// ---- +// Warning 4984: (129-134): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol new file mode 100644 index 000000000..a834311cf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + require(x + y >= x); + return x + y; + } +} +// ---- +// Warning 3944: (111-116): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 3944: (133-138): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol new file mode 100644 index 000000000..d773af2f6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x % y; + } +} +// ---- +// Warning 3046: (110-115): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol new file mode 100644 index 000000000..34cc55a7b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x * y; + } +} +// ---- +// Warning 3944: (110-115): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 4984: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol new file mode 100644 index 000000000..4a207c53f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x - y; + } +} +// ---- +// Warning 3944: (110-115): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 4984: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol new file mode 100644 index 000000000..3edc402eb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x + y; + } +} +// ---- +// Warning 3944: (110-115): Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here +// Warning 4984: (110-115): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol index 117554eca..41ba3d0d5 100644 --- a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol @@ -3,4 +3,4 @@ contract C { function f(uint a, uint b) public pure returns (uint) { return a + b; } } // ---- -// Warning 2661: (112-117): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (112-117): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol b/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol index 1029e90a5..1ecf1e870 100644 --- a/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol +++ b/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 4144: (117-122): Underflow (resulting value less than 0) happens here -// Warning 4144: (150-157): Underflow (resulting value less than 0) happens here +// Warning 3944: (117-122): Underflow (resulting value less than 0) happens here +// Warning 3944: (150-157): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol b/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol index 84634a213..6dab43c79 100644 --- a/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol @@ -16,6 +16,6 @@ contract C } } // ---- -// Warning 4144: (116-123): Underflow (resulting value less than -128) happens here -// Warning 4144: (163-170): Underflow (resulting value less than -128) happens here -// Warning 2661: (207-217): Overflow (resulting value larger than 127) happens here +// Warning 3944: (116-123): Underflow (resulting value less than -128) happens here +// Warning 3944: (163-170): Underflow (resulting value less than -128) happens here +// Warning 4984: (207-217): Overflow (resulting value larger than 127) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol new file mode 100644 index 000000000..01eeb27ac --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x / y; + } +} +// ---- +// Warning 3046: (113-118): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol new file mode 100644 index 000000000..879587e06 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + require(x >= y); + return x - y; + } +} diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol new file mode 100644 index 000000000..68b782eca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + require(x + y >= x); + return x + y; + } +} diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol new file mode 100644 index 000000000..327e2bdcc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x % y; + } +} +// ---- +// Warning 3046: (113-118): Division by zero happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol new file mode 100644 index 000000000..bf7b697e8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x * y; + } +} +// ---- +// Warning 4984: (113-118): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol new file mode 100644 index 000000000..6a44c893b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x - y; + } +} +// ---- +// Warning 3944: (113-118): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol new file mode 100644 index 000000000..b0632ef48 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x + y; + } +} +// ---- +// Warning 4984: (113-118): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol index 1fbd2f905..2e9b3c79a 100644 --- a/test/libsolidity/smtCheckerTests/special/many.sol +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -15,6 +15,7 @@ contract C } } // ---- +// Warning 4984: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (79-115): Assertion violation happens here // Warning 6328: (119-161): Assertion violation happens here // Warning 6328: (165-204): Assertion violation happens here @@ -23,4 +24,3 @@ contract C // Warning 6328: (304-332): Assertion violation happens here // Warning 6328: (336-364): Assertion violation happens here // Warning 6328: (368-391): Assertion violation happens here -// Warning 2661: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/types/address_balance.sol b/test/libsolidity/smtCheckerTests/types/address_balance.sol index 8edae0c8c..c4d10fead 100644 --- a/test/libsolidity/smtCheckerTests/types/address_balance.sol +++ b/test/libsolidity/smtCheckerTests/types/address_balance.sol @@ -9,5 +9,5 @@ contract C } // ---- // Warning 2072: (96-102): Unused local variable. +// Warning 4984: (105-127): Overflow (resulting value larger than 2**256 - 1) happens here // Warning 6328: (131-160): Assertion violation happens here -// Warning 2661: (105-127): Overflow (resulting value larger than 2**256 - 1) 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 6b09f780d..f267f0b99 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -26,4 +26,4 @@ contract C } } // ---- -// Warning 6328: (400-457): Assertion violation happens here +// Warning 6328: (400-457): Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol index ec8c795a5..e99eea071 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol @@ -18,4 +18,3 @@ contract c { // ---- // Warning 6328: (288-324): Assertion violation happens here // Warning 6328: (336-372): Assertion violation happens here -// Warning 6031: (166-178): Internal error: Expression undefined for SMT solver. diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol new file mode 100644 index 000000000..15e9c4dfd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + uint[][] a; + function f(uint[1] memory x) public { + a.push(x); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol new file mode 100644 index 000000000..2a267cdd9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + uint[][] a; + function f(uint[1][] memory x) public { + a.push(x[2]); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol new file mode 100644 index 000000000..09b8a9773 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract D { + bytes16[] inner; + bytes32[][] data; + function t() public { + data.push(inner); + } +} + diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol new file mode 100644 index 000000000..cbfd162ca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract D { + int16[] inner; + int[][] data; + function t() public { + data.push(inner); + } +} + diff --git a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol index 0a89c9975..9cea35cbb 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 2661: (152-157): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (152-157): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol new file mode 100644 index 000000000..b0861b3b5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns(int) { + int a; + (,, a) = ((((((1, 3, (((((2))))))))))); + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning 6328: (157-171): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol new file mode 100644 index 000000000..02a14c1b4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns(int) { + int a; + ((,, a)) = ((((((1, 3, (((((2))))))))))); + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning 6328: (159-173): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol new file mode 100644 index 000000000..824d84c3e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C { + function g() internal pure returns (uint, uint) { + return (2, 3); + } + function f() public { + (address(1).call("")); + (uint x, uint y) = ((g())); + assert(x == 2); + assert(y == 3); + } +} +// ---- +// Warning 5084: (142-152): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/syntaxTests/bound/interface_using_for.sol b/test/libsolidity/syntaxTests/bound/interface_using_for.sol new file mode 100644 index 000000000..727ff2c46 --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/interface_using_for.sol @@ -0,0 +1,10 @@ +library L { + function f() public {} +} + +interface I { + using L for int; + function g() external; +} +// ---- +// TypeError 9088: (60-76): The "using for" directive is not allowed inside interfaces. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol new file mode 100644 index 000000000..749a98d48 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure {} +} +function fun() { + C.f(); +} +// ---- +// TypeError 3419: (68-73): Cannot call function via contract type name. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol new file mode 100644 index 000000000..afb375ca0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol @@ -0,0 +1,3 @@ +constructor() {} +// ---- +// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol new file mode 100644 index 000000000..31b410211 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol @@ -0,0 +1,3 @@ +fallback(){} +// ---- +// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol new file mode 100644 index 000000000..dd702853a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol @@ -0,0 +1,4 @@ +function fun() someModifier { +} +// ---- +// DeclarationError 7576: (15-27): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol new file mode 100644 index 000000000..779d44c72 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol @@ -0,0 +1,9 @@ +contract C { + modifier someModifier() { _; } +} + +function fun() C.someModifier { + +} +// ---- +// ParserError 2314: (65-66): Expected '{' but got '.' diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol new file mode 100644 index 000000000..bd1b2bd87 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol @@ -0,0 +1,9 @@ +function f() {} +contract C { + function f() public {} + function g() public { + f(); + } +} +// ---- +// Warning 2519: (31-53): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol new file mode 100644 index 000000000..36a0e0e0c --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol @@ -0,0 +1,5 @@ +function fun1() public { } +function fun2() internal { } +// ---- +// SyntaxError 4126: (0-26): Free functions cannot have visibility. +// SyntaxError 4126: (27-55): Free functions cannot have visibility. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol new file mode 100644 index 000000000..2fec9db1d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol @@ -0,0 +1,3 @@ +function f(); +// ---- +// TypeError 4668: (0-13): Free functions must be implemented. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol new file mode 100644 index 000000000..4862e8e63 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol @@ -0,0 +1,4 @@ +function fun(uint256, uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol new file mode 100644 index 000000000..dafa91ccb --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol @@ -0,0 +1,8 @@ +function f() { + uint x = 2; + x; +} +function g(uint[] storage x) pure { x[0] = 1; } +// ---- +// Warning 2018: (0-39): Function state mutability can be restricted to pure +// TypeError 8961: (76-80): Function declared as pure, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol new file mode 100644 index 000000000..440b9449f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol @@ -0,0 +1,9 @@ +function f(uint) returns (bytes memory) {} +function f(uint[] memory x) returns (bytes memory) { return f(x[0]); } +function g(uint8) {} +function g(uint16) {} +function t() { + g(2); +} +// ---- +// TypeError 4487: (176-177): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_override.sol b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol new file mode 100644 index 000000000..da54a2fa6 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol @@ -0,0 +1,4 @@ +function fun() override { +} +// ---- +// SyntaxError 1750: (0-27): Free functions cannot override. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol new file mode 100644 index 000000000..563c118f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol @@ -0,0 +1,4 @@ +function fun() payable { +} +// ---- +// TypeError 9559: (0-26): Free functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol new file mode 100644 index 000000000..5801b9e9e --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol @@ -0,0 +1,3 @@ +receive() {} +// ---- +// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol new file mode 100644 index 000000000..7d356a9a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol @@ -0,0 +1,4 @@ +struct S { uint x; } +function fun(S storage) { +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol new file mode 100644 index 000000000..a4261be1f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol @@ -0,0 +1,4 @@ +function fun() virtual { +} +// ---- +// SyntaxError 4493: (0-26): Free functions cannot be virtual. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol new file mode 100644 index 000000000..3b16749ae --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol @@ -0,0 +1,4 @@ +contract C {} +function C() {} +// ---- +// DeclarationError 2333: (14-29): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol new file mode 100644 index 000000000..b029c0d90 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint x; } +} +function f() returns (uint) { S storage t; } +// ---- +// DeclarationError 7920: (70-71): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol new file mode 100644 index 000000000..7e2dafe8a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol @@ -0,0 +1,6 @@ +function fun() { + fun{gas: 1}(); + fun{value: 1}(); +} +// ---- +// TypeError 2193: (21-32): Function call options can only be set on external function calls or contract creations. diff --git a/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol new file mode 100644 index 000000000..ab027e21d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol @@ -0,0 +1,7 @@ +function f() returns (uint) { C.S storage t; t.x; } + +contract C { + struct S { uint x; } +} +// ---- +// TypeError 3464: (45-46): 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/freeFunctions/struct_after_function.sol b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol new file mode 100644 index 000000000..c41693e97 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol @@ -0,0 +1,3 @@ +function f(S storage g) view returns (uint) { S storage t = g; return t.x; } +struct S { uint x; } +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol new file mode 100644 index 000000000..ff6061c65 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + super; +} +// ---- +// DeclarationError 7576: (33-38): Undeclared identifier. "super" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol new file mode 100644 index 000000000..cdea708d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + this; +} +// ---- +// DeclarationError 7576: (33-37): Undeclared identifier. "this" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol b/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol index 15118322f..4c3d45385 100644 --- a/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol +++ b/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol @@ -4,11 +4,15 @@ contract b { } c d; + function e() public view { c storage x = d; - x.a[0]; + x.a[0]; + function()[3**44] storage fs; } } // ---- // Warning 3408: (66-69): Variable "d" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 2332: (110-111): Type "b.c" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2332: (111-112): Type "struct b.c" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2332: (152-169): Type "function ()[984770902183611232881]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2072: (152-180): Unused local variable. diff --git a/test/libsolidity/syntaxTests/immutable/complex.sol b/test/libsolidity/syntaxTests/immutable/complex.sol new file mode 100644 index 000000000..b624430ea --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/complex.sol @@ -0,0 +1,8 @@ +contract A { + int immutable a; + constructor() { a = 5; } + function f() public { a += 7; } +} + +// ---- +// TypeError 1581: (83-84): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol index b4808b844..052a1b597 100644 --- a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol +++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 4599: (86-87): Immutable variables must be initialized unconditionally, not in an if statement. +// TypeError 4599: (86-87): Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol index f91e98333..e95bf5740 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// TypeError 1581: (119-120): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (119-120): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol index 3f2461456..0a966f7c3 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol @@ -9,4 +9,4 @@ contract C { function f(uint a) internal pure {} } // ---- -// TypeError 1581: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (59-60): Cannot write to immutable here: 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 index 14f47364a..5970ab247 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// TypeError 1581: (102-103): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (102-103): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol index 3c34dd3b7..5717f05c8 100644 --- a/test/libsolidity/syntaxTests/immutable/decrement.sol +++ b/test/libsolidity/syntaxTests/immutable/decrement.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { x--; } } // ---- -// TypeError 7733: (67-68): 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. +// TypeError 7733: (63-64): 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 index 58e9235f3..541fceb53 100644 --- a/test/libsolidity/syntaxTests/immutable/delete.sol +++ b/test/libsolidity/syntaxTests/immutable/delete.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { delete x; } } // ---- -// TypeError 7733: (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. +// TypeError 7733: (70-71): 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_and_initialize.sol b/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol new file mode 100644 index 000000000..859a30a77 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x = 3; + constructor() { + delete x; + } +} +// ---- +// TypeError 1574: (74-75): Immutable state variable already initialized. +// TypeError 7733: (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/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol index 629d80f66..cc175d083 100644 --- a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol @@ -10,5 +10,4 @@ contract C is B(C.f) { function f() internal returns(uint) { return x = 2; } } // ---- -// TypeError 1581: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (200-201): Immutable state variable already initialized. +// TypeError 1581: (200-201): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol index 00c20422c..11959ebc7 100644 --- a/test/libsolidity/syntaxTests/immutable/increment.sol +++ b/test/libsolidity/syntaxTests/immutable/increment.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { x++; } } // ---- -// TypeError 7733: (67-68): 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. +// TypeError 7733: (63-64): 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/increment_decrement.sol b/test/libsolidity/syntaxTests/immutable/increment_decrement.sol new file mode 100644 index 000000000..ccfaf1920 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/increment_decrement.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + uint immutable y; + constructor() { + ++x; + --y; + } +} +// ---- +// TypeError 7733: (77-78): 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. +// TypeError 7733: (86-87): 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_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol index 695319378..bc084400e 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol @@ -11,4 +11,4 @@ contract C is B { constructor() B(y = 3) { } } // ---- -// TypeError 1581: (148-149): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (148-149): Cannot write to immutable here: 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 index 8e3d67145..503f182d6 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol @@ -10,4 +10,4 @@ contract C is B(C.y = 3) { uint immutable y; } // ---- -// TypeError 1581: (104-107): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (104-107): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol index 4f431474b..748d9da55 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol @@ -8,5 +8,4 @@ contract C is B { } } // ---- -// TypeError 7484: (88-89): Immutable variables must be initialized in the constructor of the contract they are defined in. -// TypeError 1574: (88-89): Immutable state variable already initialized. +// TypeError 7484: (88-89): Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in. diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol index b831f0f0b..d8c8ca47f 100644 --- a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol +++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 6672: (88-89): Immutable variables can only be initialized once, not in a while statement. +// TypeError 6672: (88-89): Cannot write to immutable here: Immutable variables cannot be initialized inside a loop. diff --git a/test/libsolidity/syntaxTests/immutable/unary.sol b/test/libsolidity/syntaxTests/immutable/unary.sol new file mode 100644 index 000000000..f3c75c09b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/unary.sol @@ -0,0 +1,8 @@ +contract A { + int immutable a; + constructor() { a = 5; } + function f() public { --a; } +} + +// ---- +// TypeError 1581: (85-86): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol new file mode 100644 index 000000000..73fe59801 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable z = 2; + uint immutable x = z = y = 3; + uint immutable y = 5; +} +// ---- +// TypeError 1581: (62-63): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (66-67): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol new file mode 100644 index 000000000..2c98ffd79 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol @@ -0,0 +1,5 @@ +contract C { + int immutable x = x = 5; +} +// ---- +// TypeError 1581: (35-36): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol index 05d366ead..4b2b91a15 100644 --- a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol @@ -6,5 +6,4 @@ contract C { } } // ---- -// TypeError 1581: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (76-77): Immutable state variable already initialized. +// TypeError 1581: (76-77): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol index cdfd47443..d450a37d2 100644 --- a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol @@ -8,5 +8,4 @@ contract C { } } // ---- -// TypeError 1581: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (111-112): Immutable state variable already initialized. +// TypeError 1581: (111-112): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/imports/importing_free_functions.sol b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol new file mode 100644 index 000000000..02bc3673e --- /dev/null +++ b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol @@ -0,0 +1,14 @@ +==== Source: a ==== +function f(uint x) pure returns (uint) { return x * 3; } +==== Source: b ==== +import "a" as A; +function g(uint x) pure returns (uint) { return A.f(x) * 3; } +==== Source: c ==== +import "b" as B; +contract C { + function f() public pure { + B.g(2); + B.A.f(3); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol new file mode 100644 index 000000000..c842060ff --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + assembly { + function f (a, b , c ) -> y,x,z { + } + } + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol new file mode 100644 index 000000000..d3c56f3e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure public { + assembly { + function f (a, b , c ) - > y,x,z { + } + } + } +} +// ---- +// ParserError 2314: (87-88): Expected '{' but got '-' diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol new file mode 100644 index 000000000..b51dd8604 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + let x := hex"0011" + } + } +} +// ---- +// ParserError 3772: (72-81): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol new file mode 100644 index 000000000..2f4f69174 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + pop(hex"2233") + } + } +} +// ---- +// ParserError 3772: (67-76): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol new file mode 100644 index 000000000..de64567d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + switch codesize() + case hex"00" {} + case hex"1122" {} + } + } +} +// ---- +// ParserError 3772: (92-99): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol new file mode 100644 index 000000000..9d9b35e28 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol @@ -0,0 +1,43 @@ +contract C { + function f() pure public { + assembly { + dup0() + dup1() + dup2() + dup3() + dup4() + dup5() + dup6() + dup7() + dup8() + dup9() + dup10() + dup11() + dup12() + dup13() + dup14() + dup15() + dup16() + dup32() + } + } +} +// ---- +// DeclarationError 4619: (75-79): Function not found. +// DeclarationError 4619: (94-98): Function not found. +// DeclarationError 4619: (113-117): Function not found. +// DeclarationError 4619: (132-136): Function not found. +// DeclarationError 4619: (151-155): Function not found. +// DeclarationError 4619: (170-174): Function not found. +// DeclarationError 4619: (189-193): Function not found. +// DeclarationError 4619: (208-212): Function not found. +// DeclarationError 4619: (227-231): Function not found. +// DeclarationError 4619: (246-250): Function not found. +// DeclarationError 4619: (265-270): Function not found. +// DeclarationError 4619: (285-290): Function not found. +// DeclarationError 4619: (305-310): Function not found. +// DeclarationError 4619: (325-330): Function not found. +// DeclarationError 4619: (345-350): Function not found. +// DeclarationError 4619: (365-370): Function not found. +// DeclarationError 4619: (385-390): Function not found. +// DeclarationError 4619: (405-410): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol new file mode 100644 index 000000000..7da213776 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol @@ -0,0 +1,9 @@ +contract C { + function f() pure public { + assembly { + jumpdest() + } + } +} +// ---- +// DeclarationError 4619: (75-83): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol new file mode 100644 index 000000000..72e3fcd98 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol @@ -0,0 +1,73 @@ +contract C { + function f() pure public { + assembly { + push0() + push1() + push2() + push3() + push4() + push5() + push6() + push7() + push8() + push9() + push10() + push11() + push12() + push13() + push14() + push15() + push16() + push17() + push18() + push19() + push20() + push21() + push22() + push23() + push24() + push25() + push26() + push27() + push28() + push29() + push30() + push31() + push32() + } + } +} +// ---- +// DeclarationError 4619: (75-80): Function not found. +// DeclarationError 4619: (95-100): Function not found. +// DeclarationError 4619: (115-120): Function not found. +// DeclarationError 4619: (135-140): Function not found. +// DeclarationError 4619: (155-160): Function not found. +// DeclarationError 4619: (175-180): Function not found. +// DeclarationError 4619: (195-200): Function not found. +// DeclarationError 4619: (215-220): Function not found. +// DeclarationError 4619: (235-240): Function not found. +// DeclarationError 4619: (255-260): Function not found. +// DeclarationError 4619: (275-281): Function not found. +// DeclarationError 4619: (296-302): Function not found. +// DeclarationError 4619: (317-323): Function not found. +// DeclarationError 4619: (338-344): Function not found. +// DeclarationError 4619: (359-365): Function not found. +// DeclarationError 4619: (380-386): Function not found. +// DeclarationError 4619: (401-407): Function not found. +// DeclarationError 4619: (422-428): Function not found. +// DeclarationError 4619: (443-449): Function not found. +// DeclarationError 4619: (464-470): Function not found. +// DeclarationError 4619: (485-491): Function not found. +// DeclarationError 4619: (506-512): Function not found. +// DeclarationError 4619: (527-533): Function not found. +// DeclarationError 4619: (548-554): Function not found. +// DeclarationError 4619: (569-575): Function not found. +// DeclarationError 4619: (590-596): Function not found. +// DeclarationError 4619: (611-617): Function not found. +// DeclarationError 4619: (632-638): Function not found. +// DeclarationError 4619: (653-659): Function not found. +// DeclarationError 4619: (674-680): Function not found. +// DeclarationError 4619: (695-701): Function not found. +// DeclarationError 4619: (716-722): Function not found. +// DeclarationError 4619: (737-743): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol new file mode 100644 index 000000000..278398218 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol @@ -0,0 +1,43 @@ +contract C { + function f() pure public { + assembly { + swap0() + swap1() + swap2() + swap3() + swap4() + swap5() + swap6() + swap7() + swap8() + swap9() + swap10() + swap11() + swap12() + swap13() + swap14() + swap15() + swap16() + swap32() + } + } +} +// ---- +// DeclarationError 4619: (75-80): Function not found. +// DeclarationError 4619: (95-100): Function not found. +// DeclarationError 4619: (115-120): Function not found. +// DeclarationError 4619: (135-140): Function not found. +// DeclarationError 4619: (155-160): Function not found. +// DeclarationError 4619: (175-180): Function not found. +// DeclarationError 4619: (195-200): Function not found. +// DeclarationError 4619: (215-220): Function not found. +// DeclarationError 4619: (235-240): Function not found. +// DeclarationError 4619: (255-260): Function not found. +// DeclarationError 4619: (275-281): Function not found. +// DeclarationError 4619: (296-302): Function not found. +// DeclarationError 4619: (317-323): Function not found. +// DeclarationError 4619: (338-344): Function not found. +// DeclarationError 4619: (359-365): Function not found. +// DeclarationError 4619: (380-386): Function not found. +// DeclarationError 4619: (401-407): Function not found. +// DeclarationError 4619: (422-428): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol new file mode 100644 index 000000000..5467d045a --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol @@ -0,0 +1,113 @@ +contract C { + function f() view public { + assembly { + // These are keywords of Solidity -- a copy from liblangutil/Token.h. + let abstract := 1 + let anonymous := 1 + let as := 1 + let assembly := 1 + // break is Yul keyword + let catch := 1 + let constant := 1 + let constructor := 1 + // continue is Yul keyword + let contract := 1 + let do := 1 + let else := 1 + let enum := 1 + let emit := 1 + let event := 1 + let external := 1 + let fallback := 1 + // for is a Yul keyword + // function is a Yul keyword + // hex is a Yul keyword + // if is a Yul keyword + let indexed := 1 + let interface := 1 + let internal := 1 + let immutable := 1 + let import := 1 + let is := 1 + let library := 1 + let mapping := 1 + let memory := 1 + let modifier := 1 + let new := 1 + let override := 1 + let payable := 1 + let public := 1 + let pragma := 1 + let private := 1 + let pure := 1 + let receive := 1 + // return is a builtin in EVMDialect + return(0, 0) + let returns := 1 + let storage := 1 + let calldata := 1 + let struct := 1 + let throw := 1 + let try := 1 + // type shadows the Solidity function + let unicode := 1 + let using := 1 + let view := 1 + let virtual := 1 + let while := 1 + let wei := 1 + let gwei := 1 + let ether := 1 + let seconds := 1 + let minutes := 1 + let hours := 1 + let days := 1 + let weeks := 1 + let years := 1 + let int := 1 + let uint := 1 + let bytes := 1 + // byte is a builtin in EVMDialect + pop(byte(1, 1)) + let string := 1 + // address is a builtin in EVMDialect + pop(address()) + let bool := 1 + let fixed := 1 + let ufixed := 1 + let after := 1 + let alias := 1 + let apply := 1 + let auto := 1 + // case is a Yul keyword + let copyof := 1 + // default is a Yul keyword + let define := 1 + let final := 1 + let implements := 1 + let in := 1 + let inline := 1 + // let is a Yul keyword + let macro := 1 + let match := 1 + let mutable := 1 + let null := 1 + let of := 1 + let partial := 1 + let promise := 1 + let reference := 1 + let relocatable := 1 + let sealed := 1 + let sizeof := 1 + let static := 1 + let supports := 1 + // switch is a Yul keyword + let typedef := 1 + let typeof := 1 + let unchecked := 1 + let var := 1 + } + } +} +// ---- +// Warning 5740: (955-2168): Unreachable code. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul b/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul new file mode 100644 index 000000000..d068a72e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + switch codesize() + case "1" {} + case "2" {} + } + } +} diff --git a/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol b/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol index b32815028..902ad73bc 100644 --- a/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol +++ b/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol @@ -3,4 +3,4 @@ contract C { function f(S storage) internal {} } // ---- -// Warning 2332: (64-65): Type "C.S" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2332: (64-65): Type "struct C.S" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol index baa4a2286..91cd21e49 100644 --- a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol +++ b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol @@ -2,6 +2,20 @@ contract Test { function f() public pure returns (string memory) { return type(C).name; } + function g() public pure returns (string memory) { + return type(A).name; + } + function h() public pure returns (string memory) { + return type(I).name; + } +} + +abstract contract A { + function f() virtual public pure; +} + +interface I { + function f() external pure; } contract C { diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol index 17508ed8d..188dda39c 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol @@ -1,8 +1,10 @@ contract test { function f() public { int x = 3; int y = 4; x ** y; } function h() public { uint8 x = 3; int16 y = 4; x ** y; } + function i() public { int16 x = 4; x ** -3; } } // ---- // TypeError 2271: (64-70): Operator ** not compatible with types int256 and int256. Exponentiation power is not allowed to be a signed integer type. // TypeError 2271: (126-132): Operator ** not compatible with types uint8 and int16. Exponentiation power is not allowed to be a signed integer type. // Warning 3149: (126-132): The result type of the exponentiation operation is equal to the type of the first operand (uint8) ignoring the (larger) type of the second operand (int16) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other. +// TypeError 2271: (175-182): Operator ** not compatible with types int16 and int_const -3. Exponentiation power is not allowed to be a negative integer literal. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol index e518021b4..d2c41cd2c 100644 --- a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol @@ -2,6 +2,18 @@ contract C { /// @inheritdoc function f() internal { } + /// @inheritdoc . + function f() internal { + } + /// @inheritdoc C..f + function f() internal { + } + /// @inheritdoc C. + function f() internal { + } } // ---- // DocstringParsingError 1933: (17-32): Expected contract name following documentation tag @inheritdoc. +// DocstringParsingError 5967: (71-88): Documentation tag @inheritdoc reference "." is malformed. +// DocstringParsingError 5967: (127-147): Documentation tag @inheritdoc reference "C..f" is malformed. +// DocstringParsingError 5967: (186-204): Documentation tag @inheritdoc reference "C." is malformed. diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol new file mode 100644 index 000000000..8b84de055 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal internal {} +} +// ---- +// ParserError 9439: (38-46): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol new file mode 100644 index 000000000..15d60c392 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal public {} +} +// ---- +// ParserError 9439: (38-44): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol new file mode 100644 index 000000000..495e3759c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol @@ -0,0 +1,5 @@ +contract C { + constructor() payable payable {} +} +// ---- +// ParserError 9680: (37-44): State mutability already specified as "payable". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol new file mode 100644 index 000000000..7d58520e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public internal {} +} +// ---- +// ParserError 9439: (36-44): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol new file mode 100644 index 000000000..c0674f2e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public public {} +} +// ---- +// ParserError 9439: (36-42): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol new file mode 100644 index 000000000..8d67db095 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() pure pure g; + } +} +// ---- +// ParserError 9680: (62-66): State mutability already specified as "pure". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol new file mode 100644 index 000000000..c9b035864 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() public public g; + } +} +// ---- +// ParserError 9439: (64-70): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol b/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol new file mode 100644 index 000000000..8dbe4ac67 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol @@ -0,0 +1,5 @@ +contract C { + modifier f() override override {} +} +// ---- +// ParserError 9102: (39-47): Override already specified. diff --git a/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol b/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol new file mode 100644 index 000000000..c04558171 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol @@ -0,0 +1,7 @@ +contract C { + uint private internal a; + function f() private external {} +} +// ---- +// ParserError 4110: (30-38): Visibility already specified as "private". +// ParserError 9439: (67-75): Visibility already specified as "private". diff --git a/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol b/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol new file mode 100644 index 000000000..f5d5077f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol @@ -0,0 +1,6 @@ +contract C { + string s = unicode"À"; +} +// ---- +// SyntaxError 8452: (28-38): Invalid UTF-8 sequence found +// TypeError 7407: (28-38): Type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type string storage ref. diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol new file mode 100644 index 000000000..6009ee94d --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol @@ -0,0 +1,10 @@ +library a { + struct b { + mapping (uint => b) c ; + } + // Segfaults in https://github.com/ethereum/solidity/issues/9443 + function d(b memory) public {} +} +// ---- +// TypeError 4103: (149-157): Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions. +// TypeError 4061: (149-157): Type struct a.b is only valid in storage because it contains a (nested) mapping. diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol new file mode 100644 index 000000000..d40a4407c --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol @@ -0,0 +1,6 @@ +library a { + struct b { + mapping (uint => b) c ; + } + function d(b storage) public {} +} diff --git a/test/libsolidity/syntaxTests/unexpected.sol b/test/libsolidity/syntaxTests/unexpected.sol index 79ee1b6cb..971c78000 100644 --- a/test/libsolidity/syntaxTests/unexpected.sol +++ b/test/libsolidity/syntaxTests/unexpected.sol @@ -1,3 +1,3 @@ unexpected // ---- -// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum definition. +// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index f961c615c..b614d17b9 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -300,7 +300,8 @@ string TestFunctionCall::formatRawParameters( { if (param.format.newline) os << endl << _linePrefix << "// "; - os << param.rawString; + for (auto const c: param.rawString) + os << (c >= ' ' ? string(1, c) : "\\x" + toHex(static_cast(c))); if (¶m != &_params.back()) os << ", "; } diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 56051ffdd..e72d98be2 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -49,14 +49,27 @@ string toString(SideEffects const& _sideEffects) vector ret; if (_sideEffects.movable) ret.emplace_back("movable"); - if (_sideEffects.sideEffectFree) - ret.emplace_back("sideEffectFree"); - if (_sideEffects.sideEffectFreeIfNoMSize) - ret.emplace_back("sideEffectFreeIfNoMSize"); - if (_sideEffects.invalidatesStorage) - ret.emplace_back("invalidatesStorage"); - if (_sideEffects.invalidatesMemory) - ret.emplace_back("invalidatesMemory"); + if (_sideEffects.movableApartFromEffects) + ret.emplace_back("movable apart from effects"); + if (_sideEffects.canBeRemoved) + ret.emplace_back("can be removed"); + if (_sideEffects.canBeRemovedIfNoMSize) + ret.emplace_back("can be removed if no msize"); + if (!_sideEffects.cannotLoop) + ret.emplace_back("can loop"); + if (_sideEffects.otherState == SideEffects::Write) + ret.emplace_back("writes other state"); + else if (_sideEffects.otherState == SideEffects::Read) + ret.emplace_back("reads other state"); + if (_sideEffects.storage == SideEffects::Write) + ret.emplace_back("writes storage"); + else if (_sideEffects.storage == SideEffects::Read) + ret.emplace_back("reads storage"); + if (_sideEffects.memory == SideEffects::Write) + ret.emplace_back("writes memory"); + else if (_sideEffects.memory == SideEffects::Read) + ret.emplace_back("reads memory"); + return joinHumanReadable(ret); } } diff --git a/test/libyul/functionSideEffects/cyclic_graph.yul b/test/libyul/functionSideEffects/cyclic_graph.yul index 58eba0acf..640bb7057 100644 --- a/test/libyul/functionSideEffects/cyclic_graph.yul +++ b/test/libyul/functionSideEffects/cyclic_graph.yul @@ -4,7 +4,7 @@ function c() { b() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: -// c: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop +// b: movable apart from effects, can loop +// c: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/doubly_recursive_function.yul b/test/libyul/functionSideEffects/doubly_recursive_function.yul index 9afcfb0af..aa137abb0 100644 --- a/test/libyul/functionSideEffects/doubly_recursive_function.yul +++ b/test/libyul/functionSideEffects/doubly_recursive_function.yul @@ -3,6 +3,6 @@ function b() { a() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop +// b: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/empty.yul b/test/libyul/functionSideEffects/empty.yul index 9e85e64b7..e3cc25d67 100644 --- a/test/libyul/functionSideEffects/empty.yul +++ b/test/libyul/functionSideEffects/empty.yul @@ -1,4 +1,4 @@ { } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize diff --git a/test/libyul/functionSideEffects/empty_with_sstore.yul b/test/libyul/functionSideEffects/empty_with_sstore.yul index 42a1b564c..b2cd4fa57 100644 --- a/test/libyul/functionSideEffects/empty_with_sstore.yul +++ b/test/libyul/functionSideEffects/empty_with_sstore.yul @@ -2,4 +2,4 @@ sstore(0, 1) } // ---- -// : invalidatesStorage +// : writes storage diff --git a/test/libyul/functionSideEffects/memory.yul b/test/libyul/functionSideEffects/memory.yul new file mode 100644 index 000000000..d39ffce68 --- /dev/null +++ b/test/libyul/functionSideEffects/memory.yul @@ -0,0 +1,14 @@ +{ + function a() { mstore8(0, 32) } + function f() { a() } + function g() { sstore(0, 1) } // does not affect memory + function h() { pop(mload(0)) } + function i() { pop(msize()) } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes memory +// f: writes memory +// g: writes storage +// h: movable apart from effects, can be removed if no msize, reads memory +// i: can be removed, can be removed if no msize, reads memory diff --git a/test/libyul/functionSideEffects/mload_in_function.yul b/test/libyul/functionSideEffects/mload_in_function.yul index 170535926..d7977e2b5 100644 --- a/test/libyul/functionSideEffects/mload_in_function.yul +++ b/test/libyul/functionSideEffects/mload_in_function.yul @@ -7,5 +7,5 @@ sstore(0, mload(0)) } // ---- -// : invalidatesStorage, invalidatesMemory -// foo: invalidatesMemory +// : can loop, writes storage, writes memory +// foo: can loop, writes memory diff --git a/test/libyul/functionSideEffects/multi_calls.yul b/test/libyul/functionSideEffects/multi_calls.yul index f60b15e3d..5d36c10df 100644 --- a/test/libyul/functionSideEffects/multi_calls.yul +++ b/test/libyul/functionSideEffects/multi_calls.yul @@ -15,8 +15,8 @@ } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: invalidatesStorage -// b: invalidatesStorage -// c: invalidatesStorage, invalidatesMemory -// d: movable, sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: can loop, writes storage +// b: can loop, writes storage +// c: can loop, writes storage, writes memory +// d: movable, movable apart from effects, can be removed, can be removed if no msize diff --git a/test/libyul/functionSideEffects/otherImmovables.yul b/test/libyul/functionSideEffects/otherImmovables.yul new file mode 100644 index 000000000..bb02dba1d --- /dev/null +++ b/test/libyul/functionSideEffects/otherImmovables.yul @@ -0,0 +1,12 @@ +{ + function a() { pop(gas()) } + function f() { a() } + function g() { stop() } + function h() { invalid() } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: can be removed, can be removed if no msize +// f: can be removed, can be removed if no msize +// g: +// h: diff --git a/test/libyul/functionSideEffects/recursive_function.yul b/test/libyul/functionSideEffects/recursive_function.yul index 875339cba..e65a00c2b 100644 --- a/test/libyul/functionSideEffects/recursive_function.yul +++ b/test/libyul/functionSideEffects/recursive_function.yul @@ -2,5 +2,5 @@ function a() { a() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/simple_functions.yul b/test/libyul/functionSideEffects/simple_functions.yul index fb0f5378e..61db9d27b 100644 --- a/test/libyul/functionSideEffects/simple_functions.yul +++ b/test/libyul/functionSideEffects/simple_functions.yul @@ -6,9 +6,9 @@ function i() { let z := mload(0) } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: movable, sideEffectFree, sideEffectFreeIfNoMSize -// f: invalidatesMemory -// g: invalidatesStorage -// h: sideEffectFree, sideEffectFreeIfNoMSize -// i: sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable, movable apart from effects, can be removed, can be removed if no msize +// f: writes memory +// g: writes storage +// h: can be removed, can be removed if no msize, reads memory +// i: movable apart from effects, can be removed if no msize, reads memory diff --git a/test/libyul/functionSideEffects/state.yul b/test/libyul/functionSideEffects/state.yul new file mode 100644 index 000000000..e605ff1e5 --- /dev/null +++ b/test/libyul/functionSideEffects/state.yul @@ -0,0 +1,10 @@ +{ + function a() { pop(call(100, 0x010, 10, 0x00, 32, 0x0100, 32))} + function f() { a() } + function g() { sstore(0, 1) } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes other state, writes storage, writes memory +// f: writes other state, writes storage, writes memory +// g: writes storage diff --git a/test/libyul/functionSideEffects/storage.yul b/test/libyul/functionSideEffects/storage.yul new file mode 100644 index 000000000..038c368a2 --- /dev/null +++ b/test/libyul/functionSideEffects/storage.yul @@ -0,0 +1,12 @@ +{ + function a() { sstore(0, 1) } + function f() { a() } + function g() { pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32))} + function h() { pop(sload(0))} +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes storage +// f: writes storage +// g: writes other state, writes storage, writes memory +// h: movable apart from effects, can be removed, can be removed if no msize, reads storage diff --git a/test/libyul/functionSideEffects/structures.yul b/test/libyul/functionSideEffects/structures.yul index de1fa5b0c..9c844f66f 100644 --- a/test/libyul/functionSideEffects/structures.yul +++ b/test/libyul/functionSideEffects/structures.yul @@ -31,10 +31,10 @@ } } // ---- -// : invalidatesStorage, invalidatesMemory -// f: sideEffectFreeIfNoMSize -// g: invalidatesStorage, invalidatesMemory -// h: invalidatesStorage, invalidatesMemory -// i: invalidatesStorage -// r: movable, sideEffectFree, sideEffectFreeIfNoMSize -// t: invalidatesMemory +// : writes storage, writes memory +// f: movable apart from effects, can be removed if no msize, reads memory +// g: writes storage, writes memory +// h: writes storage, writes memory +// i: writes storage +// r: movable, movable apart from effects, can be removed, can be removed if no msize +// t: writes memory diff --git a/test/libyul/functionSideEffects/with_loop.yul b/test/libyul/functionSideEffects/with_loop.yul index d68aa1fec..6e9bc6436 100644 --- a/test/libyul/functionSideEffects/with_loop.yul +++ b/test/libyul/functionSideEffects/with_loop.yul @@ -4,6 +4,6 @@ pop(f()) } // ---- -// : -// f: -// g: +// : movable apart from effects, can loop +// f: movable apart from effects, can loop +// g: movable apart from effects, can loop diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index 98c8e3010..df3119103 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -36,20 +36,15 @@ // function f(x) // { // mstore(0, x) -// let t_20 := 0 -// t_20 := 2 -// mstore(7, t_20) -// g(10) +// let t_14 := 0 +// t_14 := 2 +// mstore(7, t_14) +// let x_1_15 := 10 +// f(1) // mstore(1, x) // } // function g(x_1) -// { -// let x_14 := 1 -// mstore(0, x_14) -// mstore(7, h()) -// g(10) -// mstore(1, x_14) -// } +// { f(1) } // function h() -> t // { t := 2 } // } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul new file mode 100644 index 000000000..3c11ca65f --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(keccak256(mul(mload(inv), 3), 32)) + a := add(x, 1) + sstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := extcodesize(keccak256(mul(mload(inv), 3), 32)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// sstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul new file mode 100644 index 000000000..f48cb78f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul @@ -0,0 +1,30 @@ +{ + function g() -> x { x := create(100, 0, 32) } + function f() -> x { x := mload(0) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + // cannot be moved because of the create call in g() + let q := sload(5) + let r := g() + // This can be moved + let z := f() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := create(100, 0, 32) } +// function f() -> x_1 +// { x_1 := mload(0) } +// let b := 1 +// let a := 1 +// let z := f() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let q := sload(5) +// let r := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul new file mode 100644 index 000000000..a4506a8e5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul @@ -0,0 +1,24 @@ +{ + function g() -> x { x := add(sload(mload(x)), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := mload(g()) + let s := keccak256(g(), 32) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := add(sload(mload(x)), 1) } +// let b := 1 +// let a := 1 +// let t := mload(g()) +// let s := keccak256(g(), 32) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul new file mode 100644 index 000000000..a5117cb61 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := mload(g()) } + function g() -> x { x := add(sload(x), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := mload(g()) } +// function g() -> x_1 +// { x_1 := add(sload(x_1), 1) } +// let b := 1 +// let a := 1 +// let t := extcodesize(f()) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul new file mode 100644 index 000000000..7790c0e6a --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := add(x, 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := sload(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := add(x_1, 1) } +// let b := 1 +// let a := 1 +// let t := sload(f()) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul new file mode 100644 index 000000000..b08899613 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul @@ -0,0 +1,29 @@ +{ + let a := 1 + function f() -> x {invalid()} + function g() -> y {return(0, 0)} + for { let i := 1 } iszero(eq(i, 10)) { a := add(i, 1) } { + let b := f() + let c := gas() + let d := g() + let e := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let a := 1 +// function f() -> x +// { invalid() } +// function g() -> y +// { return(0, 0) } +// let i := 1 +// for { } iszero(eq(i, 10)) { a := add(i, 1) } +// { +// let b := f() +// let c := gas() +// let d := g() +// let e := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul new file mode 100644 index 000000000..942a3c580 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul @@ -0,0 +1,38 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := keccak256(inv, 32) + a := add(x, 1) + mstore(a, inv) + } + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + // mload prevents moving of extcodesize + let x := extcodesize(mload(mul(inv, 3))) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := keccak256(inv, 32) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := extcodesize(mload(mul(inv_2, 3))) +// a_1 := add(x_3, 1) +// mstore(a_1, inv_2) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul new file mode 100644 index 000000000..1ebef6f9c --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := mload(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := mload(f()) +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul new file mode 100644 index 000000000..2033a9be9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul @@ -0,0 +1,35 @@ +{ + let b := 1 + let c := msize() // prevents moving keccak and mload + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := keccak256(inv, 32) + a := add(x, 1) + } + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(mload(mul(inv, 3))) + a := add(x, 1) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let c := msize() +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := keccak256(inv, 32) +// a := add(x, 1) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := extcodesize(mload(mul(inv_2, 3))) +// a_1 := add(x_3, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul new file mode 100644 index 000000000..fa6c985b4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul @@ -0,0 +1,62 @@ +{ + let b := 1 + // invalidates state in post + for { let a := 1 } iszero(eq(a, 10)) {pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32))} { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + pop(create(0x08, 0x00, 0x02)) // invalidates state + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + // invalidates state in loop-condition + for { let a := 1 } iszero(create(0x08, 0x00, 0x02)) { a := add(a, 1)} { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } +// iszero(eq(a, 10)) +// { +// pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32)) +// } +// { +// let x := extcodesize(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// pop(create(0x08, 0x00, 0x02)) +// let x_3 := extcodesize(mul(inv_2, 3)) +// a_1 := add(x_3, 1) +// mstore(a_1, inv_2) +// } +// let a_4 := 1 +// let inv_5 := add(b, 42) +// for { } iszero(create(0x08, 0x00, 0x02)) { a_4 := add(a_4, 1) } +// { +// let x_6 := extcodesize(mul(inv_5, 3)) +// a_4 := add(x_6, 1) +// mstore(a_4, inv_5) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul new file mode 100644 index 000000000..ab5be62d9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul @@ -0,0 +1,32 @@ +{ + function f() -> x { x := g() } + function g() -> x { + x := add(x, 1) + sstore(0x00, 0x00) + } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// x_1 := add(x_1, 1) +// sstore(0x00, 0x00) +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul new file mode 100644 index 000000000..d7e16ed68 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul new file mode 100644 index 000000000..431453941 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul @@ -0,0 +1,26 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := g() } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := g() } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul new file mode 100644 index 000000000..ae20f4904 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul @@ -0,0 +1,32 @@ +{ + let b := 1 + // invalidates state in post + for { let a := 1 } iszero(eq(a, 10)) {pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32))} { + let inv := add(b, 42) + let x := returndatasize() + a := add(x, 1) + pop(staticcall(2, 3, 0, 32, 64, 32)) // prevents moving returndatasize + mstore(a, inv) + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } +// iszero(eq(a, 10)) +// { +// pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32)) +// } +// { +// let x := returndatasize() +// a := add(x, 1) +// pop(staticcall(2, 3, 0, 32, 64, 32)) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul new file mode 100644 index 000000000..0ef2fd78a --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul @@ -0,0 +1,57 @@ +{ + let b := 1 + // invalidates storage in post + for { let a := 1 } iszero(eq(a, 10)) { sstore(0x00, 0x01)} { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + // invalidates storage in body + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + sstore(a, inv) + } + + // invalidates state in body + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32)) + let x := sload(mul(inv, 3)) + a := add(x, 1) + } + +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { sstore(0x00, 0x01) } +// { +// let x := sload(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := sload(mul(inv_2, 3)) +// a_1 := add(x_3, 1) +// sstore(a_1, inv_2) +// } +// let a_4 := 1 +// let inv_5 := add(b, 42) +// for { } iszero(eq(a_4, 10)) { a_4 := add(a_4, 1) } +// { +// pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32)) +// let x_6 := sload(mul(inv_5, 3)) +// a_4 := add(x_6, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul new file mode 100644 index 000000000..f1a2d6283 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul @@ -0,0 +1,28 @@ +{ + function f() -> x { x := g() } + function g() -> x { + x := add(x, 1) + sstore(0x00, 0x00) + } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// x_1 := add(x_1, 1) +// sstore(0x00, 0x00) +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { let q := sload(g()) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul new file mode 100644 index 000000000..d799fa6ae --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := sload(f()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { let t := sload(f()) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul new file mode 100644 index 000000000..2840d3445 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul @@ -0,0 +1,21 @@ +{ + function g() -> x { x := add(mload(x), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + sstore(0, a) + let q := keccak256(g(), 32) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := add(mload(x), 1) } +// let b := 1 +// let a := 1 +// let q := keccak256(g(), 32) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { sstore(0, a) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul new file mode 100644 index 000000000..223f54ee7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul @@ -0,0 +1,25 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := mload(mul(inv, 3)) + let y := keccak256(mul(b, 13), 32) + a := add(x, 1) + sstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := mload(mul(inv, 3)) +// let y := keccak256(mul(b, 13), 32) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// sstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul new file mode 100644 index 000000000..f8b5a3d53 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := extcodesize(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul new file mode 100644 index 000000000..022d66b2d --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := sload(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulSyntaxTests/datacopy_shadowing.yul b/test/libyul/yulSyntaxTests/datacopy_shadowing.yul new file mode 100644 index 000000000..3f14f4722 --- /dev/null +++ b/test/libyul/yulSyntaxTests/datacopy_shadowing.yul @@ -0,0 +1,5 @@ +{ + function datacopy(a, b, c) {} +} +// ---- +// ParserError 5568: (15-23): Cannot use builtin function name "datacopy" as identifier name. diff --git a/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul b/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul new file mode 100644 index 000000000..fb32fb61c --- /dev/null +++ b/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul @@ -0,0 +1,5 @@ +{ + function dataoffset(a) -> b {} +} +// ---- +// ParserError 5568: (15-25): Cannot use builtin function name "dataoffset" as identifier name. diff --git a/test/libyul/yulSyntaxTests/datasize_shadowing.yul b/test/libyul/yulSyntaxTests/datasize_shadowing.yul new file mode 100644 index 000000000..379401754 --- /dev/null +++ b/test/libyul/yulSyntaxTests/datasize_shadowing.yul @@ -0,0 +1,5 @@ +{ + function datasize(a) -> b {} +} +// ---- +// ParserError 5568: (15-23): Cannot use builtin function name "datasize" as identifier name. diff --git a/test/libyul/yulSyntaxTests/function_definition.yul b/test/libyul/yulSyntaxTests/function_definition.yul new file mode 100644 index 000000000..9cd21193e --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definition.yul @@ -0,0 +1,4 @@ +{ + function f (a, b , c ) -> y,x,z { + } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/function_definition_whitespace.yul b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul new file mode 100644 index 000000000..1c397982b --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul @@ -0,0 +1,6 @@ +{ + function f (a, b , c ) - > y,x,z { + } +} +// ---- +// ParserError 2314: (30-31): Expected '{' but got '-' diff --git a/test/libyul/yulSyntaxTests/hex_assignment.yul b/test/libyul/yulSyntaxTests/hex_assignment.yul new file mode 100644 index 000000000..213163916 --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_assignment.yul @@ -0,0 +1,5 @@ +{ + let x := hex"0011" +} +// ---- +// ParserError 3772: (15-24): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/hex_expression.yul b/test/libyul/yulSyntaxTests/hex_expression.yul new file mode 100644 index 000000000..191fdf085 --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_expression.yul @@ -0,0 +1,5 @@ +{ + pop(hex"2233") +} +// ---- +// ParserError 3772: (10-19): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/hex_switch_case.yul b/test/libyul/yulSyntaxTests/hex_switch_case.yul new file mode 100644 index 000000000..87ba6a4a9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_switch_case.yul @@ -0,0 +1,7 @@ +{ + switch codesize() + case hex"00" {} + case hex"1122" {} +} +// ---- +// ParserError 3772: (33-40): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul new file mode 100644 index 000000000..ffcd60c3e --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul @@ -0,0 +1,39 @@ +{ + dup0() + dup1() + dup2() + dup3() + dup4() + dup5() + dup6() + dup7() + dup8() + dup9() + dup10() + dup11() + dup12() + dup13() + dup14() + dup15() + dup16() + dup32() +} +// ---- +// DeclarationError 4619: (6-10): Function not found. +// DeclarationError 4619: (17-21): Function not found. +// DeclarationError 4619: (28-32): Function not found. +// DeclarationError 4619: (39-43): Function not found. +// DeclarationError 4619: (50-54): Function not found. +// DeclarationError 4619: (61-65): Function not found. +// DeclarationError 4619: (72-76): Function not found. +// DeclarationError 4619: (83-87): Function not found. +// DeclarationError 4619: (94-98): Function not found. +// DeclarationError 4619: (105-109): Function not found. +// DeclarationError 4619: (116-121): Function not found. +// DeclarationError 4619: (128-133): Function not found. +// DeclarationError 4619: (140-145): Function not found. +// DeclarationError 4619: (152-157): Function not found. +// DeclarationError 4619: (164-169): Function not found. +// DeclarationError 4619: (176-181): Function not found. +// DeclarationError 4619: (188-193): Function not found. +// DeclarationError 4619: (200-205): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul b/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul new file mode 100644 index 000000000..c3b5e02c2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul @@ -0,0 +1,5 @@ +{ + let x := 0100 +} +// ---- +// ParserError 1856: (15-16): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul new file mode 100644 index 000000000..5ab50a4c6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul @@ -0,0 +1,5 @@ +{ + jump(2) +} +// ---- +// DeclarationError 4619: (6-10): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul new file mode 100644 index 000000000..579f34892 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul @@ -0,0 +1,5 @@ +{ + jumpdest() +} +// ---- +// DeclarationError 4619: (6-14): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul new file mode 100644 index 000000000..e22a86ce1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul @@ -0,0 +1,5 @@ +{ + jumpi(2, 1) +} +// ---- +// DeclarationError 4619: (6-11): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul new file mode 100644 index 000000000..63762bd31 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul @@ -0,0 +1,5 @@ +{ + label: +} +// ---- +// ParserError 6913: (11-12): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul b/test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul new file mode 100644 index 000000000..dca68b7a0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/leave_items_on_tack.yul @@ -0,0 +1,5 @@ +{ + mload(0) +} +// ---- +// TypeError 3083: (6-14): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. diff --git a/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul new file mode 100644 index 000000000..f57faf32a --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul @@ -0,0 +1,5 @@ +{ + 1 +} +// ---- +// ParserError 6913: (8-9): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/pc.yul b/test/libyul/yulSyntaxTests/invalid/pc_disallowed.yul similarity index 100% rename from test/libyul/yulSyntaxTests/pc.yul rename to test/libyul/yulSyntaxTests/invalid/pc_disallowed.yul diff --git a/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul new file mode 100644 index 000000000..c863e58c8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul @@ -0,0 +1,69 @@ +{ + push0() + push1() + push2() + push3() + push4() + push5() + push6() + push7() + push8() + push9() + push10() + push11() + push12() + push13() + push14() + push15() + push16() + push17() + push18() + push19() + push20() + push21() + push22() + push23() + push24() + push25() + push26() + push27() + push28() + push29() + push30() + push31() + push32() +} +// ---- +// DeclarationError 4619: (6-11): Function not found. +// DeclarationError 4619: (18-23): Function not found. +// DeclarationError 4619: (30-35): Function not found. +// DeclarationError 4619: (42-47): Function not found. +// DeclarationError 4619: (54-59): Function not found. +// DeclarationError 4619: (66-71): Function not found. +// DeclarationError 4619: (78-83): Function not found. +// DeclarationError 4619: (90-95): Function not found. +// DeclarationError 4619: (102-107): Function not found. +// DeclarationError 4619: (114-119): Function not found. +// DeclarationError 4619: (126-132): Function not found. +// DeclarationError 4619: (139-145): Function not found. +// DeclarationError 4619: (152-158): Function not found. +// DeclarationError 4619: (165-171): Function not found. +// DeclarationError 4619: (178-184): Function not found. +// DeclarationError 4619: (191-197): Function not found. +// DeclarationError 4619: (204-210): Function not found. +// DeclarationError 4619: (217-223): Function not found. +// DeclarationError 4619: (230-236): Function not found. +// DeclarationError 4619: (243-249): Function not found. +// DeclarationError 4619: (256-262): Function not found. +// DeclarationError 4619: (269-275): Function not found. +// DeclarationError 4619: (282-288): Function not found. +// DeclarationError 4619: (295-301): Function not found. +// DeclarationError 4619: (308-314): Function not found. +// DeclarationError 4619: (321-327): Function not found. +// DeclarationError 4619: (334-340): Function not found. +// DeclarationError 4619: (347-353): Function not found. +// DeclarationError 4619: (360-366): Function not found. +// DeclarationError 4619: (373-379): Function not found. +// DeclarationError 4619: (386-392): Function not found. +// DeclarationError 4619: (399-405): Function not found. +// DeclarationError 4619: (412-418): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul new file mode 100644 index 000000000..4103cab6f --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul @@ -0,0 +1,39 @@ +{ + swap0() + swap1() + swap2() + swap3() + swap4() + swap5() + swap6() + swap7() + swap8() + swap9() + swap10() + swap11() + swap12() + swap13() + swap14() + swap15() + swap16() + swap32() +} +// ---- +// DeclarationError 4619: (6-11): Function not found. +// DeclarationError 4619: (18-23): Function not found. +// DeclarationError 4619: (30-35): Function not found. +// DeclarationError 4619: (42-47): Function not found. +// DeclarationError 4619: (54-59): Function not found. +// DeclarationError 4619: (66-71): Function not found. +// DeclarationError 4619: (78-83): Function not found. +// DeclarationError 4619: (90-95): Function not found. +// DeclarationError 4619: (102-107): Function not found. +// DeclarationError 4619: (114-119): Function not found. +// DeclarationError 4619: (126-132): Function not found. +// DeclarationError 4619: (139-145): Function not found. +// DeclarationError 4619: (152-158): Function not found. +// DeclarationError 4619: (165-171): Function not found. +// DeclarationError 4619: (178-184): Function not found. +// DeclarationError 4619: (191-197): Function not found. +// DeclarationError 4619: (204-210): Function not found. +// DeclarationError 4619: (217-223): Function not found. diff --git a/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul b/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul new file mode 100644 index 000000000..68ce920e1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul @@ -0,0 +1,7 @@ +{ + function linkersymbol(a) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-27): Cannot use builtin function name "linkersymbol" as identifier name. diff --git a/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul b/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul new file mode 100644 index 000000000..8711d0b17 --- /dev/null +++ b/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul @@ -0,0 +1,7 @@ +{ + function loadimmutable(a) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-28): Cannot use builtin function name "loadimmutable" as identifier name. diff --git a/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul b/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul new file mode 100644 index 000000000..b76a5de9b --- /dev/null +++ b/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul @@ -0,0 +1,7 @@ +{ + function setimmutable(a, b) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-27): Cannot use builtin function name "setimmutable" as identifier name. diff --git a/test/libyul/yulSyntaxTests/solidity_keywords.yul b/test/libyul/yulSyntaxTests/solidity_keywords.yul new file mode 100644 index 000000000..1f1dafc53 --- /dev/null +++ b/test/libyul/yulSyntaxTests/solidity_keywords.yul @@ -0,0 +1,109 @@ +{ + // These are keywords of Solidity -- a copy from liblangutil/Token.h. + let abstract := 1 + let anonymous := 1 + let as := 1 + let assembly := 1 + // break is Yul keyword + let catch := 1 + let constant := 1 + let constructor := 1 + // continue is Yul keyword + let contract := 1 + let do := 1 + let else := 1 + let enum := 1 + let emit := 1 + let event := 1 + let external := 1 + let fallback := 1 + // for is a Yul keyword + // function is a Yul keyword + // hex is a Yul keyword + // if is a Yul keyword + let indexed := 1 + let interface := 1 + let internal := 1 + let immutable := 1 + let import := 1 + let is := 1 + let library := 1 + let mapping := 1 + let memory := 1 + let modifier := 1 + let new := 1 + let override := 1 + let payable := 1 + let public := 1 + let pragma := 1 + let private := 1 + let pure := 1 + let receive := 1 + // return is a builtin in EVMDialect + return(0, 0) + let returns := 1 + let storage := 1 + let calldata := 1 + let struct := 1 + let throw := 1 + let try := 1 + let type := 1 + let unicode := 1 + let using := 1 + let view := 1 + let virtual := 1 + let while := 1 + let wei := 1 + let gwei := 1 + let ether := 1 + let seconds := 1 + let minutes := 1 + let hours := 1 + let days := 1 + let weeks := 1 + let years := 1 + let int := 1 + let uint := 1 + let bytes := 1 + // byte is a builtin in EVMDialect + pop(byte(1, 1)) + let string := 1 + // address is a builtin in EVMDialect + pop(address()) + let bool := 1 + let fixed := 1 + let ufixed := 1 + let after := 1 + let alias := 1 + let apply := 1 + let auto := 1 + // case is a Yul keyword + let copyof := 1 + // default is a Yul keyword + let define := 1 + let final := 1 + let implements := 1 + let in := 1 + let inline := 1 + // let is a Yul keyword + let macro := 1 + let match := 1 + let mutable := 1 + let null := 1 + let of := 1 + let partial := 1 + let promise := 1 + let reference := 1 + let relocatable := 1 + let sealed := 1 + let sizeof := 1 + let static := 1 + let supports := 1 + // switch is a Yul keyword + let typedef := 1 + let typeof := 1 + let unchecked := 1 + let var := 1 +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/string_literal_switch_case.yul b/test/libyul/yulSyntaxTests/string_literal_switch_case.yul new file mode 100644 index 000000000..02d1045c3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_switch_case.yul @@ -0,0 +1,5 @@ +{ + switch codesize() + case "1" {} + case "2" {} +} \ No newline at end of file diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index a799bd736..372e899a3 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -23,12 +23,34 @@ if (OSSFUZZ) endif() if (OSSFUZZ) - add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp) - target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm) + add_executable(solc_opt_ossfuzz + solc_opt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + SolidityLexer.cpp + SolidityParser.cpp + ) + target_compile_options(solc_opt_ossfuzz + PUBLIC + ${COMPILE_OPTIONS} -Wno-extra-semi -Wno-unused-parameter + ) + target_include_directories(solc_opt_ossfuzz PRIVATE /usr/include/antlr4-runtime) + target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm antlr4-runtime) set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp) - target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm) + add_executable(solc_noopt_ossfuzz + solc_noopt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + SolidityLexer.cpp + SolidityParser.cpp + ) + target_compile_options(solc_noopt_ossfuzz + PUBLIC + ${COMPILE_OPTIONS} -Wno-extra-semi -Wno-unused-parameter + ) + target_include_directories(solc_noopt_ossfuzz PRIVATE /usr/include/antlr4-runtime) + target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm antlr4-runtime) set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) add_executable(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp)