mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #9536 from ethereum/develop
Merge develop into release for 0.7.0
This commit is contained in:
		
						commit
						9e61f92bd4
					
				| @ -9,16 +9,20 @@ version: 2.1 | ||||
| parameters: | ||||
|   ubuntu-1804-docker-image: | ||||
|     type: string | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4484ac3da8fdc337cc77a7a7be1af71cd0f28f9c890d934f1d6ae7532beb66b1" | ||||
|     # solbuildpackpusher/solidity-buildpack-deps:ubuntu1804-2 | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9ab317e583c395e50884ba82e9f99811c374344cea4c550725be8ec836e07acc" | ||||
|   ubuntu-2004-docker-image: | ||||
|     type: string | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:48b5bb6b91ac7dddfe9345c88876ebed126c652258800f114caed69db73b29bf" | ||||
|     # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-2 | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:cbfa42d8ecbe94391ba8837e218869242666de7a0da6ccac065a856c85b6a6a0" | ||||
|   ubuntu-2004-clang-docker-image: | ||||
|     type: string | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d8775de58167db5a11690fdb6ef639317fe1e579ec5d46e9732d2d903b55d135" | ||||
|     # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-2 | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a4d5271b5552139d9f2caefc50d42f401bf74132cf8f253e199e11c80ab42de" | ||||
|   ubuntu-1604-clang-ossfuzz-docker-image: | ||||
|     type: string | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:db52f3257396814215744a19904e42c07e040ab36b68be72a27ba71ad2f1083c" | ||||
|     # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-2 | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:efaabb3c143f64171be596932c62013bcfd7f73b1fbcb832025a34dd2b6e6922" | ||||
|   emscripten-docker-image: | ||||
|     type: string | ||||
|     default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc" | ||||
| @ -385,11 +389,13 @@ jobs: | ||||
|       - run: *run_docs_pragma_min_version | ||||
| 
 | ||||
|   b_ubu_clang: &build_ubuntu2004_clang | ||||
|     resource_class: xlarge | ||||
|     docker: | ||||
|       - image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> | ||||
|     environment: | ||||
|       CC: clang | ||||
|       CXX: clang++ | ||||
|       MAKEFLAGS: -j 10 | ||||
|     steps: | ||||
|       - checkout | ||||
|       - run: *run_build | ||||
|  | ||||
| @ -10,7 +10,7 @@ include(EthPolicy) | ||||
| eth_policy() | ||||
| 
 | ||||
| # project name and version should be set after cmake_policy CMP0048 | ||||
| set(PROJECT_VERSION "0.6.12") | ||||
| set(PROJECT_VERSION "0.7.0") | ||||
| # OSX target needed in order to support std::visit | ||||
| set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") | ||||
| project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) | ||||
|  | ||||
							
								
								
									
										47
									
								
								Changelog.md
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								Changelog.md
									
									
									
									
									
								
							| @ -1,3 +1,49 @@ | ||||
| ### 0.7.0 (2020-07-28) | ||||
| 
 | ||||
| Breaking changes: | ||||
|  * Inline Assembly: Disallow ``.`` in user-defined function and variable names. | ||||
|  * Inline Assembly: Slot and offset of storage pointer variable ``x`` are accessed via ``x.slot`` and ``x.offset`` instead of ``x_slot`` and ``x_offset``. | ||||
|  * JSON AST: Mark hex string literals with ``kind: "hexString"``. | ||||
|  * JSON AST: Remove members with ``null`` value from JSON output. | ||||
|  * Parser: Disallow ``gwei`` as identifier. | ||||
|  * Parser: Disallow dot syntax for ``value`` and ``gas``. | ||||
|  * Parser: Disallow non-printable characters in string literals. | ||||
|  * Parser: Introduce Unicode string literals: ``unicode"😃"``. | ||||
|  * Parser: NatSpec comments on variables are only allowed for public state variables. | ||||
|  * Parser: Remove the ``finney`` and ``szabo`` denominations. | ||||
|  * Parser: Remove the identifier ``now`` (replaced by ``block.timestamp``). | ||||
|  * Reference Resolver: ``using A for B`` only affects the contract it is mentioned in and not all derived contracts | ||||
|  * Type Checker: Disallow ``virtual`` for library functions. | ||||
|  * Type Checker: Disallow assignments to state variables that contain nested mappings. | ||||
|  * Type checker: Disallow events with same name and parameter types in inheritance hierarchy. | ||||
|  * Type Checker: Disallow shifts by signed types. | ||||
|  * Type Checker: Disallow structs and arrays in memory or calldata if they contain nested mappings. | ||||
|  * Type Checker: Exponentiation and shifts of literals by non-literals will always use ``uint256`` or ``int256`` as a type. | ||||
|  * Yul: Disallow consecutive and trailing dots in identifiers. Leading dots were already disallowed. | ||||
|  * Yul: Disallow EVM instruction `pc()`. | ||||
| 
 | ||||
| 
 | ||||
| Language Features: | ||||
|  * Inheritance: Allow overrides to have stricter state mutability: ``view`` can override ``nonpayable`` and ``pure`` can override ``view``. | ||||
|  * Parser: Deprecate visibility for constructors. | ||||
|  * State mutability: Do not issue recommendation for stricter mutability for virtual functions but do issue it for functions that override. | ||||
| 
 | ||||
| 
 | ||||
| Compiler Features: | ||||
|  * SMTChecker: Report multi-transaction counterexamples including the function calls that initiate the transactions. This does not include concrete values for reference types and reentrant calls. | ||||
|  * Variable declarations using the ``var`` keyword are not recognized anymore. | ||||
| 
 | ||||
| 
 | ||||
| Bugfixes: | ||||
|  * Immutables: Fix internal compiler error when immutables are not assigned. | ||||
|  * Inheritance: Disallow public state variables overwriting ``pure`` functions. | ||||
|  * NatSpec: Constructors and functions have consistent userdoc output. | ||||
|  * SMTChecker: Fix internal error when assigning to a 1-tuple. | ||||
|  * SMTChecker: Fix internal error when tuples have extra effectless parenthesis. | ||||
|  * State Mutability: Constant public state variables are considered ``pure`` functions. | ||||
|  * Type Checker: Fixing deduction issues on function types when function call has named arguments. | ||||
| 
 | ||||
| 
 | ||||
| ### 0.6.12 (2020-07-22) | ||||
| 
 | ||||
| Language Features: | ||||
| @ -29,6 +75,7 @@ Build System: | ||||
| 
 | ||||
| ### 0.6.11 (2020-07-07) | ||||
| 
 | ||||
| 
 | ||||
| Language Features: | ||||
|  * General: Add unit denomination ``gwei`` | ||||
|  * Yul: Support ``linkersymbol`` builtin in standalone assembly mode to refer to library addresses. | ||||
|  | ||||
| @ -37,7 +37,7 @@ found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/ | ||||
| A "Hello World" program in Solidity is of even less use than in other languages, but still: | ||||
| 
 | ||||
| ```solidity | ||||
| pragma solidity ^0.6.0; | ||||
| pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
| contract HelloWorld { | ||||
|   function helloWorld() external pure returns (string memory) { | ||||
|  | ||||
| @ -26,15 +26,26 @@ | ||||
|  - [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. Make sure to create ``prerelease.txt`` before: (``echo -n > prerelease.txt``). This will create the tarball in a directory called ``upload``. | ||||
|  - [ ] Take the tarball from the upload directory (its name should be ``solidity_x.x.x.tar.gz``, otherwise ``prerelease.txt`` was missing in the step before) and upload the source tarball to the release page. | ||||
| 
 | ||||
| ### Homebrew and MacOS | ||||
|  - [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb | ||||
|  - [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``. | ||||
| 
 | ||||
| ### Update solc-bin | ||||
|  - [ ] Copy ``soljson.js`` from the release page to ``solc-bin/bin/soljson-v<version>+commit.<commit>.js`` | ||||
|  - [ ] Copy ``solc-static-linux`` from the release page to ``solc-bin/linux-amd64/solc-linux-amd64-v<version>+commit.<commit>`` | ||||
|  - [ ] Copy ``solc-macos`` from the release page to ``solc-bin/macos-amd64/solc-macos-amd64-v<version>+commit.<commit>`` | ||||
|  - [ ] Copy ``solc-windows.zip`` from the release page to ``solc-bin/windows-amd64/solc-windows-amd64-v<version>+commit.<commit>.zip`` | ||||
|  - [ ] Make the linux and the macos binaries executable. | ||||
|  - [ ] Run ``./update`` in ``solc-bin`` and verify that the script has updated ``list.js``, ``list.txt`` and ``list.json`` files correctly and that symlinks to the new release have been added in ``solc-bin/wasm/`` and ``solc-bin/emscripten-wasm32/``. | ||||
|  - [ ] Create a pull request and merge. | ||||
| 
 | ||||
| ### PPA | ||||
|  - [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id. | ||||
|  - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). | ||||
|  - [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``. | ||||
|  - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``). | ||||
| 
 | ||||
| ### Homebrew and MacOS | ||||
|  - [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb | ||||
|  - [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``. | ||||
| ### Docker | ||||
|  - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``). | ||||
| 
 | ||||
| ### Documentation | ||||
|  - [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`) | ||||
| @ -46,6 +57,7 @@ | ||||
|  - [ ] Make sure to push the tag ``npm publish`` created with ``git push --tags``. | ||||
| 
 | ||||
| ### Post-release | ||||
|  - [ ] Publish the blog post. | ||||
|  - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. | ||||
|  - [ ] Merge ``release`` back into ``develop``. | ||||
|  - [ ] Announce on Twitter and Reddit. | ||||
|  | ||||
| @ -311,7 +311,7 @@ This will no longer compile with Solidity v0.5.0. However, you can define a comp | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
|     interface OldContract { | ||||
|         function someOldFunction(uint8 a) external; | ||||
|         function anotherOldFunction() external returns (bool); | ||||
| @ -329,7 +329,7 @@ Given the interface defined above, you can now easily use the already deployed p | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     interface OldContract { | ||||
|         function someOldFunction(uint8 a) external; | ||||
|  | ||||
							
								
								
									
										133
									
								
								docs/070-breaking-changes.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								docs/070-breaking-changes.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,133 @@ | ||||
| ******************************** | ||||
| Solidity v0.7.0 Breaking Changes | ||||
| ******************************** | ||||
| 
 | ||||
| This section highlights the main breaking changes introduced in Solidity | ||||
| version 0.7.0, along with the reasoning behind the changes and how to update | ||||
| affected code. | ||||
| For the full list check | ||||
| `the release changelog <https://github.com/ethereum/solidity/releases/tag/v0.7.0>`_. | ||||
| 
 | ||||
| 
 | ||||
| Silent Changes of the Semantics | ||||
| =============================== | ||||
| 
 | ||||
| * Exponentiation and shifts of literals by non-literals (e.g. ``1 << x`` or ``2 ** x``) | ||||
|   will always use either the type ``uint256`` (for non-negative literals) or | ||||
|   ``int256`` (for negative literals) to perform the operation. | ||||
|   Previously, the operation was performed in the type of the shift amount / the | ||||
|   exponent which can be misleading. | ||||
| 
 | ||||
| 
 | ||||
| Changes to the Syntax | ||||
| ===================== | ||||
| 
 | ||||
| * In external function and contract creation calls, Ether and gas is now specified using a new syntax: | ||||
|   ``x.f{gas: 10000, value: 2 ether}(arg1, arg2)``. | ||||
|   The old syntax -- ``x.f.gas(10000).value(2 ether)(arg1, arg2)`` -- will cause an error. | ||||
| 
 | ||||
| * The global variable ``now`` is deprecated, ``block.timestamp`` should be used instead. | ||||
|   The single identifier ``now`` is too generic for a global variable and could give the impression | ||||
|   that it changes during transaction processing, whereas ``block.timestamp`` correctly | ||||
|   reflects the fact that it is just a property of the block. | ||||
| 
 | ||||
| * NatSpec comments on variables are only allowed for public state variables and not | ||||
|   for local or internal variables. | ||||
| 
 | ||||
| * The token ``gwei`` is a keyword now (used to specify, e.g. ``2 gwei`` as a number) | ||||
|   and cannot be used as an identifier. | ||||
| 
 | ||||
| * String literals now can only contain printable ASCII characters and this also includes a variety of | ||||
|   escape sequences, such as hexadecimal (``\xff``) and unicode escapes (``\u20ac``). | ||||
| 
 | ||||
| * Unicode string literals are supported now to accommodate valid UTF-8 sequences. They are identified | ||||
|   with the ``unicode`` prefix: ``unicode"Hello 😃"``. | ||||
| 
 | ||||
| * State Mutability: The state mutability of functions can now be restricted during inheritance: | ||||
|   Functions with default state mutability can be overridden by ``pure`` and ``view`` functions | ||||
|   while ``view`` functions can be overridden by ``pure`` functions. | ||||
|   At the same time, public state variables are considered ``view`` and even ``pure`` | ||||
|   if they are constants. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Inline Assembly | ||||
| --------------- | ||||
| 
 | ||||
| * Disallow ``.`` in user-defined function and variable names in inline assembly. | ||||
|   It is still valid if you use Solidity in Yul-only mode. | ||||
| 
 | ||||
| * Slot and offset of storage pointer variable ``x`` are accessed via ``x.slot`` | ||||
|   and ``x.offset`` instead of ``x_slot`` and ``x_offset``. | ||||
| 
 | ||||
| Removal of Unused or Unsafe Features | ||||
| ==================================== | ||||
| 
 | ||||
| Mappings outside Storage | ||||
| ------------------------ | ||||
| 
 | ||||
| * If a struct or array contains a mapping, it can only be used in storage. | ||||
|   Previously, mapping members were silently skipped in memory, which | ||||
|   is confusing and error-prone. | ||||
| 
 | ||||
| * Assignments to structs or arrays in storage does not work if they contain | ||||
|   mappings. | ||||
|   Previously, mappings were silently skipped during the copy operation, which | ||||
|   is misleading and error-prone. | ||||
| 
 | ||||
| Functions and Events | ||||
| -------------------- | ||||
| 
 | ||||
| * Visibility (``public`` / ``external``) is not needed for constructors anymore: | ||||
|   To prevent a contract from being created, it can be marked ``abstract``. | ||||
|   This makes the visibility concept for constructors obsolete. | ||||
| 
 | ||||
| * Type Checker: Disallow ``virtual`` for library functions: | ||||
|   Since libraries cannot be inherited from, library functions should not be virtual. | ||||
| 
 | ||||
| * Multiple events with the same name and parameter types in the same | ||||
|   inheritance hierarchy are disallowed. | ||||
| 
 | ||||
| * ``using A for B`` only affects the contract it is mentioned in. | ||||
|   Previously, the effect was inherited. Now, you have to repeat the ``using`` | ||||
|   statement in all derived contracts that make use of the feature. | ||||
| 
 | ||||
| Expressions | ||||
| ----------- | ||||
| 
 | ||||
| * Shifts by signed types are disallowed. | ||||
|   Previously, shifts by negative amounts were allowed, but reverted at runtime. | ||||
| 
 | ||||
| * The ``finney`` and ``szabo`` denominations are removed. | ||||
|   They are rarely used and do not make the actual amount readily visible. Instead, explicit | ||||
|   values like ``1e20`` or the very common ``gwei`` can be used. | ||||
| 
 | ||||
| Declarations | ||||
| ------------ | ||||
| 
 | ||||
| * The keyword ``var`` cannot be used anymore. | ||||
|   Previously, this keyword would parse but result in a type error and | ||||
|   a suggestion about which type to use. Now, it results in a parser error. | ||||
| 
 | ||||
| Interface Changes | ||||
| ================= | ||||
| 
 | ||||
| * JSON AST: Mark hex string literals with ``kind: "hexString"``. | ||||
| * JSON AST: Members with value ``null`` are removed from JSON output. | ||||
| * NatSpec: Constructors and functions have consistent userdoc output. | ||||
| 
 | ||||
| 
 | ||||
| How to update your code | ||||
| ======================= | ||||
| 
 | ||||
| This section gives detailed instructions on how to update prior code for every breaking change. | ||||
| 
 | ||||
| * Change ``x.f.value(...)()`` to ``x.f{value: ...}()``. Similarly ``(new C).value(...)()`` to | ||||
|   ``new C{value: ...}()`` and ``x.f.gas(...).value(...)()`` to ``x.f{gas: ..., value: ...}()``. | ||||
| * Change ``now`` to ``block.timestamp``. | ||||
| * Change types of right operand in shift operators to unsigned types. For example change ``x >> (256 - y)`` to | ||||
|   ``x >> uint(256 - y)``. | ||||
| * Repeat the ``using A for B`` statements in all derived contracts if needed. | ||||
| * Remove the ``public`` keyword from every constructor. | ||||
| * Remove the ``internal`` keyword from every constructor and add ``abstract`` to the contract (if not already present). | ||||
| * Change ``_slot`` and ``_offset`` suffixes in inline assembly to ``.slot`` and ``.offset``, respectively. | ||||
| @ -253,6 +253,7 @@ primaryExpression | ||||
|   | numberLiteral | ||||
|   | hexLiteral | ||||
|   | stringLiteral | ||||
|   | unicodeStringLiteral | ||||
|   | identifier ('[' ']')? | ||||
|   | TypeKeyword | ||||
|   | tupleExpression | ||||
| @ -306,7 +307,7 @@ assemblyBlock | ||||
|   : '{' assemblyItem* '}' ; | ||||
| 
 | ||||
| assemblyExpression | ||||
|   : assemblyCall | assemblyLiteral ; | ||||
|   : assemblyCall | assemblyLiteral | assemblyIdentifier ; | ||||
| 
 | ||||
| assemblyCall | ||||
|   : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; | ||||
| @ -318,7 +319,10 @@ assemblyAssignment | ||||
|   : assemblyIdentifierList ':=' assemblyExpression ; | ||||
| 
 | ||||
| assemblyIdentifierList | ||||
|   : identifier ( ',' identifier )* ; | ||||
|   : assemblyIdentifier ( ',' assemblyIdentifier )* ; | ||||
| 
 | ||||
| assemblyIdentifier | ||||
|   : identifier ( '.' identifier )* ; | ||||
| 
 | ||||
| assemblyStackAssignment | ||||
|   : '=:' identifier ; | ||||
| @ -358,11 +362,12 @@ assemblyType | ||||
| subAssembly | ||||
|   : 'assembly' identifier assemblyBlock ; | ||||
| 
 | ||||
| // 'finney' and 'szabo' are no longer supported as denominations by latest Solidity. | ||||
| numberLiteral | ||||
|   : (DecimalNumber | HexNumber) (NumberUnit | Gwei)?; | ||||
|   : (DecimalNumber | HexNumber) (NumberUnit | Gwei | Finney | Szabo)?; | ||||
| 
 | ||||
| identifier | ||||
|   : (Gwei | 'from' | 'calldata' | 'address' | Identifier) ; | ||||
|   : (Gwei | Finney | Szabo | 'from' | 'calldata' | 'address' | Identifier) ; | ||||
| 
 | ||||
| BooleanLiteral | ||||
|   : 'true' | 'false' ; | ||||
| @ -382,10 +387,12 @@ HexDigits | ||||
|   : HexCharacter ( '_'? HexCharacter )* ; | ||||
| 
 | ||||
| NumberUnit | ||||
|   : 'wei' | 'szabo' | 'finney' | 'ether' | ||||
|   : 'wei' | 'ether' | ||||
|   | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; | ||||
| 
 | ||||
| Gwei: 'gwei' ; | ||||
| Szabo: 'szabo' ; | ||||
| Finney: 'finney' ; | ||||
| 
 | ||||
| HexLiteralFragment | ||||
|   : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; | ||||
| @ -455,6 +462,13 @@ StringLiteralFragment | ||||
|   : '"' DoubleQuotedStringCharacter* '"' | ||||
|   | '\'' SingleQuotedStringCharacter* '\'' ; | ||||
| 
 | ||||
| unicodeStringLiteral | ||||
|   : UnicodeStringLiteralFragment+ ; | ||||
| 
 | ||||
| UnicodeStringLiteralFragment | ||||
|   : 'unicode"' DoubleQuotedStringCharacter* '"' | ||||
|   | 'unicode\'' SingleQuotedStringCharacter* '\'' ; | ||||
| 
 | ||||
| fragment | ||||
| DoubleQuotedStringCharacter | ||||
|   : ~["\r\n\\] | ('\\' .) ; | ||||
|  | ||||
| @ -233,7 +233,7 @@ Given the contract: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract Foo { | ||||
|         function bar(bytes3[2] memory) public pure {} | ||||
| @ -537,11 +537,11 @@ For example, | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     contract Test { | ||||
|         constructor() public { b = hex"12345678901234567890123456789012"; } | ||||
|         constructor() { b = hex"12345678901234567890123456789012"; } | ||||
|         event Event(uint indexed a, bytes32 b); | ||||
|         event Event2(uint indexed a, bytes32 b); | ||||
|         function foo(uint a) public { emit Event(a, b); } | ||||
| @ -586,7 +586,7 @@ As an example, the code | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.19 <0.7.0; | ||||
|     pragma solidity >=0.4.19 <0.8.0; | ||||
|     pragma experimental ABIEncoderV2; | ||||
| 
 | ||||
|     contract Test { | ||||
|  | ||||
| @ -42,7 +42,7 @@ without a compiler change. | ||||
| .. code:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     library GetCode { | ||||
|         function at(address _addr) public view returns (bytes memory o_code) { | ||||
| @ -68,7 +68,7 @@ efficient code, for example: | ||||
| .. code:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     library VectorSum { | ||||
| @ -132,14 +132,15 @@ For local storage variables or state variables, a single Yul identifier | ||||
| is not sufficient, since they do not necessarily occupy a single full storage slot. | ||||
| Therefore, their "address" is composed of a slot and a byte-offset | ||||
| inside that slot. To retrieve the slot pointed to by the variable ``x``, you | ||||
| use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``. | ||||
| use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``. | ||||
| Using ``x`` itself will result in an error. | ||||
| 
 | ||||
| Local Solidity variables are available for assignments, for example: | ||||
| 
 | ||||
| .. code:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint b; | ||||
| @ -147,7 +148,7 @@ Local Solidity variables are available for assignments, for example: | ||||
|             assembly { | ||||
|                 // We ignore the storage slot offset, we know it is zero | ||||
|                 // in this special case. | ||||
|                 r := mul(x, sload(b_slot)) | ||||
|                 r := mul(x, sload(b.slot)) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -164,20 +165,21 @@ Local Solidity variables are available for assignments, for example: | ||||
|     ``assembly { signextend(<num_bytes_of_x_minus_one>, x) }`` | ||||
| 
 | ||||
| 
 | ||||
| Since Solidity 0.6.0 the name of a inline assembly variable may not end in ``_offset`` or ``_slot`` | ||||
| and it may not shadow any declaration visible in the scope of the inline assembly block | ||||
| (including variable, contract and function declarations). Similarly, if the name of a declared | ||||
| variable contains a dot ``.``, the prefix up to the ``.`` may not conflict with any | ||||
| declaration visible in the scope of the inline assembly block. | ||||
| Since Solidity 0.6.0 the name of a inline assembly variable may not | ||||
| shadow any declaration visible in the scope of the inline assembly block | ||||
| (including variable, contract and function declarations). | ||||
| 
 | ||||
| Since Solidity 0.7.0, variables and functions declared inside the | ||||
| inline assembly block may not contain ``.``, but using ``.`` is | ||||
| valid to access Solidity variables from outside the inline assembly block. | ||||
| 
 | ||||
| Assignments are possible to assembly-local variables and to function-local | ||||
| variables. Take care that when you assign to variables that point to | ||||
| memory or storage, you will only change the pointer and not the data. | ||||
| 
 | ||||
| You can assign to the ``_slot`` part of a local storage variable pointer. | ||||
| For these (structs, arrays or mappings), the ``_offset`` part is always zero. | ||||
| It is not possible to assign to the ``_slot`` or ``_offset`` part of a state variable, | ||||
| You can assign to the ``.slot`` part of a local storage variable pointer. | ||||
| For these (structs, arrays or mappings), the ``.offset`` part is always zero. | ||||
| It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable, | ||||
| though. | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										84
									
								
								docs/brand-guide.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								docs/brand-guide.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,84 @@ | ||||
| #################### | ||||
| Solidity Brand Guide | ||||
| #################### | ||||
| 
 | ||||
| This brand guide features information on Solidity's brand policy and | ||||
| logo usage guidelines. | ||||
| 
 | ||||
| The Solidity Brand | ||||
| ================== | ||||
| 
 | ||||
| The Solidity programming language is an open-source, community project | ||||
| governed by a core team. The core team is sponsored by the `Ethereum | ||||
| Foundation <https://ethereum.foundation/>`_. | ||||
| 
 | ||||
| This document aims to provide information about how to best use the | ||||
| Solidity brand name and logo. | ||||
| 
 | ||||
| We encourage you to read this document carefully before using the | ||||
| brand name or the logo. Your cooperation is highly appreciated! | ||||
| 
 | ||||
| Solidity Brand Name | ||||
| =================== | ||||
| 
 | ||||
| "Solidity" should be used to refer to the Solidity programming language | ||||
| solely. | ||||
| 
 | ||||
| Please do not use "Solidity": | ||||
| 
 | ||||
| - To refer to any other programming language. | ||||
| 
 | ||||
| - In a way that is misleading or may imply association of unrelated | ||||
|   modules, tools, documentation, or other resources with the Solidity | ||||
|   programming language. | ||||
| 
 | ||||
| - In ways that confuse the community as to whether the Solidity | ||||
|   programming language is open-source and free to use. | ||||
| 
 | ||||
| Solidity Logo License | ||||
| ===================== | ||||
| 
 | ||||
| .. image:: https://i.creativecommons.org/l/by/4.0/88x31.png | ||||
|   :width: 88 | ||||
|   :alt: Creative Commons License | ||||
| 
 | ||||
| The Solidity logo is distributed and licensed under a `Creative Commons | ||||
| Attribution 4.0 International License <http://creativecommons.org/licenses/by/4.0/>`_. | ||||
| 
 | ||||
| This is the most permissive Creative Commons license and allows reuse | ||||
| and modifications for any purpose. | ||||
| 
 | ||||
| You are free to: | ||||
| 
 | ||||
| - **Share** — Copy and redistribute the material in any medium or format. | ||||
| 
 | ||||
| - **Adapt** — Remix, transform, and build upon the material for any | ||||
|   purpose, even commercially. | ||||
| 
 | ||||
| Under the following terms: | ||||
| 
 | ||||
| - **Attribution** — You must give appropriate credit, provide a link to | ||||
|   the license, and indicate if changes were made. You may do so in any | ||||
|   reasonable manner, but not in any way that suggests the the Solidity | ||||
|   core team endorses you or your use. | ||||
| 
 | ||||
| When using the Solidity logo, please respect the Solidity logo guidelines. | ||||
| 
 | ||||
| Solidity Logo Guidelines | ||||
| ======================== | ||||
| 
 | ||||
| .. image:: logo.svg | ||||
|   :width: 256 | ||||
| 
 | ||||
| Please do not: | ||||
| 
 | ||||
| - Change the ratio of the logo (do not stretch it or cut it). | ||||
| 
 | ||||
| - Change the colors of the logo, unless it is absolutely necessary. | ||||
| 
 | ||||
| Credits | ||||
| ======= | ||||
| 
 | ||||
| This document was, in parts, derived from the `Python Software | ||||
| Foundation Trademark Usage Policy <https://www.python.org/psf/trademarks/>`_ | ||||
| and the `Rust Media Guide <https://www.rust-lang.org/policies/media-guide>`_. | ||||
| @ -1181,5 +1181,9 @@ | ||||
|             "UsingForCalldata" | ||||
|         ], | ||||
|         "released": "2020-06-04" | ||||
|     }, | ||||
|     "0.7.0": { | ||||
|         "bugs": [], | ||||
|         "released": "2020-07-28" | ||||
|     } | ||||
| } | ||||
| @ -67,7 +67,7 @@ The following is the order of precedence for operators, listed in order of evalu | ||||
| | *15*       | Comma operator                      | ``,``                                      | | ||||
| +------------+-------------------------------------+--------------------------------------------+ | ||||
| 
 | ||||
| .. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send | ||||
| .. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, send | ||||
| 
 | ||||
| Global Variables | ||||
| ================ | ||||
| @ -91,7 +91,6 @@ Global Variables | ||||
| - ``msg.data`` (``bytes``): complete calldata | ||||
| - ``msg.sender`` (``address payable``): sender of the message (current call) | ||||
| - ``msg.value`` (``uint``): number of wei sent with the message | ||||
| - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) | ||||
| - ``tx.gasprice`` (``uint``): gas price of the transaction | ||||
| - ``tx.origin`` (``address payable``): sender of the transaction (full call chain) | ||||
| - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) | ||||
| @ -126,7 +125,7 @@ Global Variables | ||||
| - ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`. | ||||
| 
 | ||||
| .. note:: | ||||
|     Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, | ||||
|     Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness, | ||||
|     unless you know what you are doing. | ||||
| 
 | ||||
|     Both the timestamp and the block hash can be influenced by miners to some degree. | ||||
| @ -146,6 +145,8 @@ Global Variables | ||||
|     In version 0.5.0, the following aliases were removed: ``suicide`` as alias for ``selfdestruct``, | ||||
|     ``msg.gas`` as alias for ``gasleft``, ``block.blockhash`` as alias for ``blockhash`` and | ||||
|     ``sha3`` as alias for ``keccak256``. | ||||
| .. note:: | ||||
|     In version 0.7.0, the alias ``now`` (for ``block.timestamp``) was removed. | ||||
| 
 | ||||
| .. index:: visibility, public, private, external, internal | ||||
| 
 | ||||
|  | ||||
| @ -28,7 +28,7 @@ you receive the funds of the person who is now the richest. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract WithdrawalContract { | ||||
|         address public richest; | ||||
| @ -36,7 +36,7 @@ you receive the funds of the person who is now the richest. | ||||
| 
 | ||||
|         mapping (address => uint) pendingWithdrawals; | ||||
| 
 | ||||
|         constructor() public payable { | ||||
|         constructor() payable { | ||||
|             richest = msg.sender; | ||||
|             mostSent = msg.value; | ||||
|         } | ||||
| @ -62,13 +62,13 @@ This is as opposed to the more intuitive sending pattern: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract SendContract { | ||||
|         address payable public richest; | ||||
|         uint public mostSent; | ||||
| 
 | ||||
|         constructor() public payable { | ||||
|         constructor() payable { | ||||
|             richest = msg.sender; | ||||
|             mostSent = msg.value; | ||||
|         } | ||||
| @ -124,14 +124,14 @@ restrictions highly readable. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract AccessRestriction { | ||||
|         // These will be assigned at the construction | ||||
|         // phase, where `msg.sender` is the account | ||||
|         // creating this contract. | ||||
|         address public owner = msg.sender; | ||||
|         uint public creationTime = now; | ||||
|         uint public creationTime = block.timestamp; | ||||
| 
 | ||||
|         // Modifiers can be used to change | ||||
|         // the body of a function. | ||||
| @ -162,7 +162,7 @@ restrictions highly readable. | ||||
| 
 | ||||
|         modifier onlyAfter(uint _time) { | ||||
|             require( | ||||
|                 now >= _time, | ||||
|                 block.timestamp >= _time, | ||||
|                 "Function called too early." | ||||
|             ); | ||||
|             _; | ||||
| @ -277,7 +277,7 @@ function finishes. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract StateMachine { | ||||
|         enum Stages { | ||||
| @ -291,7 +291,7 @@ function finishes. | ||||
|         // This is the current stage. | ||||
|         Stages public stage = Stages.AcceptingBlindedBids; | ||||
| 
 | ||||
|         uint public creationTime = now; | ||||
|         uint public creationTime = block.timestamp; | ||||
| 
 | ||||
|         modifier atStage(Stages _stage) { | ||||
|             require( | ||||
| @ -310,10 +310,10 @@ function finishes. | ||||
|         // will not take the new stage into account. | ||||
|         modifier timedTransitions() { | ||||
|             if (stage == Stages.AcceptingBlindedBids && | ||||
|                         now >= creationTime + 10 days) | ||||
|                         block.timestamp >= creationTime + 10 days) | ||||
|                 nextStage(); | ||||
|             if (stage == Stages.RevealBids && | ||||
|                     now >= creationTime + 12 days) | ||||
|                     block.timestamp >= creationTime + 12 days) | ||||
|                 nextStage(); | ||||
|             // The other stages transition by transaction | ||||
|             _; | ||||
|  | ||||
| @ -14,7 +14,7 @@ defined as abstract, because the function ``utterance()`` was defined, but no im | ||||
| provided (no implementation body ``{ }`` was given).:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     abstract contract Feline { | ||||
|         function utterance() public virtual returns (bytes32); | ||||
| @ -24,14 +24,14 @@ Such abstract contracts can not be instantiated directly. This is also true, if | ||||
| all defined functions. The usage of an abstract contract as a base class is shown in the following example:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     abstract contract Feline { | ||||
|         function utterance() public virtual returns (bytes32); | ||||
|         function utterance() public pure virtual returns (bytes32); | ||||
|     } | ||||
| 
 | ||||
|     contract Cat is Feline { | ||||
|         function utterance() public override returns (bytes32) { return "miaow"; } | ||||
|         function utterance() public pure override returns (bytes32) { return "miaow"; } | ||||
|     } | ||||
| 
 | ||||
| If a contract inherits from an abstract contract and does not implement all non-implemented | ||||
|  | ||||
| @ -12,13 +12,21 @@ for ``immutable``, it can still be assigned at construction time. | ||||
| The compiler does not reserve a storage slot for these variables, and every occurrence is | ||||
| replaced by the respective value. | ||||
| 
 | ||||
| Compared to regular state variables, the gas costs of constant and immutable variables | ||||
| are much lower. For a constant variable, the expression assigned to it is copied to | ||||
| all the places where it is accessed and also re-evaluated each time. This allows for local | ||||
| optimizations. Immutable variables are evaluated once at construction time and their value | ||||
| is copied to all the places in the code where they are accessed. For these values, | ||||
| 32 bytes are reserved, even if they would fit in fewer bytes. Due to this, constant values | ||||
| 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 <strings>`_ (only for constants) and `value types <value-types>`_. | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >0.6.4 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint constant X = 32**22 + 8; | ||||
| @ -28,7 +36,7 @@ Not all types for constants and immutables are implemented at this time. The onl | ||||
|         uint immutable maxBalance; | ||||
|         address immutable owner = msg.sender; | ||||
| 
 | ||||
|         constructor(uint _decimals, address _reference) public { | ||||
|         constructor(uint _decimals, address _reference) { | ||||
|             decimals = _decimals; | ||||
|             // Assignments to immutables can even access the environment. | ||||
|             maxBalance = _reference.balance; | ||||
| @ -45,7 +53,7 @@ Constant | ||||
| 
 | ||||
| For ``constant`` variables, the value has to be a constant at compile time and it has to be | ||||
| assigned where the variable is declared. Any expression | ||||
| that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or | ||||
| that accesses storage, blockchain data (e.g. ``block.timestamp``, ``address(this).balance`` or | ||||
| ``block.number``) or | ||||
| execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions | ||||
| that might have a side-effect on memory allocation are allowed, but those that | ||||
|  | ||||
| @ -35,7 +35,7 @@ This means that cyclic creation dependencies are impossible. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     contract OwnedToken { | ||||
| @ -48,7 +48,7 @@ This means that cyclic creation dependencies are impossible. | ||||
| 
 | ||||
|         // This is the constructor which registers the | ||||
|         // creator and the assigned name. | ||||
|         constructor(bytes32 _name) public { | ||||
|         constructor(bytes32 _name) { | ||||
|             // State variables are accessed via their name | ||||
|             // and not via e.g. `this.owner`. Functions can | ||||
|             // be accessed directly or through `this.f`, | ||||
|  | ||||
| @ -66,7 +66,7 @@ is that they are cheaper to deploy and call. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.21 <0.7.0; | ||||
|     pragma solidity >=0.4.21 <0.8.0; | ||||
| 
 | ||||
|     contract ClientReceipt { | ||||
|         event Deposit( | ||||
| @ -140,7 +140,7 @@ as topics. The event call above can be performed in the same way as | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.10 <0.7.0; | ||||
|     pragma solidity >=0.4.10 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f() public payable { | ||||
|  | ||||
| @ -18,10 +18,10 @@ if they are marked ``virtual``. For details, please see | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract owned { | ||||
|         constructor() public { owner = msg.sender; } | ||||
|         constructor() { owner = msg.sender; } | ||||
|         address payable owner; | ||||
| 
 | ||||
|         // This contract only defines a modifier but does not use | ||||
| @ -63,7 +63,7 @@ if they are marked ``virtual``. For details, please see | ||||
|         mapping (address => bool) registeredAddresses; | ||||
|         uint price; | ||||
| 
 | ||||
|         constructor(uint initialPrice) public { price = initialPrice; } | ||||
|         constructor(uint initialPrice) { price = initialPrice; } | ||||
| 
 | ||||
|         // It is important to also provide the | ||||
|         // `payable` keyword here, otherwise the function will | ||||
|  | ||||
| @ -24,7 +24,7 @@ For example, if you want your contract to accept one kind of external call | ||||
| with two integers, you would use something like the following:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract Simple { | ||||
|         uint sum; | ||||
| @ -57,7 +57,7 @@ For example, suppose you want to return two results: the sum and the product of | ||||
| two integers passed as function parameters, then you use something like:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract Simple { | ||||
|         function arithmetic(uint _a, uint _b) | ||||
| @ -82,7 +82,7 @@ or you can provide return values | ||||
| statement:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract Simple { | ||||
|         function arithmetic(uint _a, uint _b) | ||||
| @ -146,11 +146,11 @@ The following statements are considered modifying the state: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f(uint a, uint b) public view returns (uint) { | ||||
|             return a * (b + 42) + now; | ||||
|             return a * (b + 42) + block.timestamp; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -192,7 +192,7 @@ In addition to the list of state modifying statements explained above, the follo | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f(uint a, uint b) public pure returns (uint) { | ||||
| @ -286,7 +286,7 @@ Below you can see an example of a Sink contract that uses function ``receive``. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     // This contract keeps all Ether sent to it with no way | ||||
|     // to get it back. | ||||
| @ -342,7 +342,7 @@ operations as long as there is enough gas passed on to it. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >=0.6.2 <0.8.0; | ||||
| 
 | ||||
|     contract Test { | ||||
|         // This function is called for all messages sent to | ||||
| @ -415,7 +415,7 @@ The following example shows overloading of the function | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|         function f(uint _in) public pure returns (uint out) { | ||||
| @ -434,7 +434,7 @@ externally visible functions differ by their Solidity types but not by their ext | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     // This will not compile | ||||
|     contract A { | ||||
| @ -468,7 +468,7 @@ candidate, resolution fails. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|         function f(uint8 _in) public pure returns (uint8 out) { | ||||
|  | ||||
| @ -39,11 +39,11 @@ Details are given in the following example. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     contract Owned { | ||||
|         constructor() public { owner = msg.sender; } | ||||
|         constructor() { owner = msg.sender; } | ||||
|         address payable owner; | ||||
|     } | ||||
| 
 | ||||
| @ -80,7 +80,7 @@ Details are given in the following example. | ||||
|     // also a base class of `Destructible`, yet there is only a single | ||||
|     // instance of `owned` (as for virtual inheritance in C++). | ||||
|     contract Named is Owned, Destructible { | ||||
|         constructor(bytes32 name) public { | ||||
|         constructor(bytes32 name) { | ||||
|             Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); | ||||
|             NameReg(config.lookup(1)).register(name); | ||||
|         } | ||||
| @ -106,8 +106,8 @@ Details are given in the following example. | ||||
| 
 | ||||
| 
 | ||||
|     // If a constructor takes an argument, it needs to be | ||||
|     // provided in the header (or modifier-invocation-style at | ||||
|     // the constructor of the derived contract (see below)). | ||||
|     // provided in the header or modifier-invocation-style at | ||||
|     // the constructor of the derived contract (see below). | ||||
|     contract PriceFeed is Owned, Destructible, Named("GoldFeed") { | ||||
|         function updateInfo(uint newInfo) public { | ||||
|             if (msg.sender == owner) info = newInfo; | ||||
| @ -127,10 +127,10 @@ destruction request. The way this is done is problematic, as | ||||
| seen in the following example:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract owned { | ||||
|         constructor() public { owner = msg.sender; } | ||||
|         constructor() { owner = msg.sender; } | ||||
|         address payable owner; | ||||
|     } | ||||
| 
 | ||||
| @ -157,10 +157,10 @@ explicitly in the final override, but this function will bypass | ||||
| ``Base1.destroy``. The way around this is to use ``super``:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract owned { | ||||
|         constructor() public { owner = msg.sender; } | ||||
|         constructor() { owner = msg.sender; } | ||||
|         address payable owner; | ||||
|     } | ||||
| 
 | ||||
| @ -203,23 +203,29 @@ Function Overriding | ||||
| 
 | ||||
| Base functions can be overridden by inheriting contracts to change their | ||||
| behavior if they are marked as ``virtual``. The overriding function must then | ||||
| use the ``override`` keyword in the function header as shown in this example: | ||||
| use the ``override`` keyword in the function header. | ||||
| The overriding function may only change the visibility of the overridden function from ``external`` to ``public``. | ||||
| The mutability may be changed to a more strict one following the order: | ||||
| ``nonpayable`` can be overridden by ``view`` and ``pure``. ``view`` can be overridden by ``pure``. | ||||
| ``payable`` is an exception and cannot be changed to any other mutability. | ||||
| 
 | ||||
| The following example demonstrates changing mutability and visibility: | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract Base | ||||
|     { | ||||
|         function foo() virtual public {} | ||||
|         function foo() virtual external view {} | ||||
|     } | ||||
| 
 | ||||
|     contract Middle is Base {} | ||||
| 
 | ||||
|     contract Inherited is Middle | ||||
|     { | ||||
|         function foo() public override {} | ||||
|         function foo() override public pure {} | ||||
|     } | ||||
| 
 | ||||
| For multiple inheritance, the most derived base contracts that define the same | ||||
| @ -232,7 +238,7 @@ bases, it has to explicitly override it: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract Base1 | ||||
|     { | ||||
| @ -259,7 +265,7 @@ that already overrides all other functions. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract A { function f() public pure{} } | ||||
|     contract B is A {} | ||||
| @ -300,11 +306,11 @@ of the variable: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract A | ||||
|     { | ||||
|         function f() external pure virtual returns(uint) { return 5; } | ||||
|         function f() external view virtual returns(uint) { return 5; } | ||||
|     } | ||||
| 
 | ||||
|     contract B is A | ||||
| @ -332,7 +338,7 @@ and the ``override`` keyword must be used in the overriding modifier: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract Base | ||||
|     { | ||||
| @ -351,7 +357,7 @@ explicitly: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract Base1 | ||||
|     { | ||||
| @ -392,33 +398,39 @@ and all functions that are reachable from there through function calls. | ||||
| It does not include the constructor code or internal functions that are | ||||
| only called from the constructor. | ||||
| 
 | ||||
| Constructor functions can be either ``public`` or ``internal``. If there is no | ||||
| If there is no | ||||
| constructor, the contract will assume the default constructor, which is | ||||
| equivalent to ``constructor() public {}``. For example: | ||||
| equivalent to ``constructor() {}``. For example: | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|     abstract contract A { | ||||
|         uint public a; | ||||
| 
 | ||||
|         constructor(uint _a) internal { | ||||
|         constructor(uint _a) { | ||||
|             a = _a; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     contract B is A(1) { | ||||
|         constructor() public {} | ||||
|         constructor() {} | ||||
|     } | ||||
| 
 | ||||
| A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`. | ||||
| You can use internal parameters in a constructor (for example storage pointers). In this case, | ||||
| the contract has to be marked :ref:`abstract <abstract-contract>`, because these parameters | ||||
| cannot be assigned valid values from outside but only through the constructors of derived contracts. | ||||
| 
 | ||||
| .. warning :: | ||||
|     Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. | ||||
|     This syntax was deprecated and is not allowed anymore in version 0.5.0. | ||||
| 
 | ||||
| .. warning :: | ||||
|     Prior to version 0.7.0, you had to specify the visibility of constructors as either | ||||
|     ``internal`` or ``public``. | ||||
| 
 | ||||
| 
 | ||||
| .. index:: ! base;constructor | ||||
| 
 | ||||
| @ -430,21 +442,21 @@ linearization rules explained below. If the base constructors have arguments, | ||||
| derived contracts need to specify all of them. This can be done in two ways:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract Base { | ||||
|         uint x; | ||||
|         constructor(uint _x) public { x = _x; } | ||||
|         constructor(uint _x) { x = _x; } | ||||
|     } | ||||
| 
 | ||||
|     // Either directly specify in the inheritance list... | ||||
|     contract Derived1 is Base(7) { | ||||
|         constructor() public {} | ||||
|         constructor() {} | ||||
|     } | ||||
| 
 | ||||
|     // or through a "modifier" of the derived constructor. | ||||
|     contract Derived2 is Base { | ||||
|         constructor(uint _y) Base(_y * _y) public {} | ||||
|         constructor(uint _y) Base(_y * _y) {} | ||||
|     } | ||||
| 
 | ||||
| One way is directly in the inheritance list (``is Base(7)``).  The other is in | ||||
| @ -490,7 +502,7 @@ error "Linearization of inheritance graph impossible". | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract X {} | ||||
|     contract A is X {} | ||||
| @ -511,14 +523,14 @@ One area where inheritance linearization is especially important and perhaps not | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract Base1 { | ||||
|         constructor() public {} | ||||
|         constructor() {} | ||||
|     } | ||||
| 
 | ||||
|     contract Base2 { | ||||
|         constructor() public {} | ||||
|         constructor() {} | ||||
|     } | ||||
| 
 | ||||
|     // Constructors are executed in the following order: | ||||
| @ -526,7 +538,7 @@ One area where inheritance linearization is especially important and perhaps not | ||||
|     //  2 - Base2 | ||||
|     //  3 - Derived1 | ||||
|     contract Derived1 is Base1, Base2 { | ||||
|         constructor() public Base1() Base2() {} | ||||
|         constructor() Base1() Base2() {} | ||||
|     } | ||||
| 
 | ||||
|     // Constructors are executed in the following order: | ||||
| @ -534,7 +546,7 @@ One area where inheritance linearization is especially important and perhaps not | ||||
|     //  2 - Base1 | ||||
|     //  3 - Derived2 | ||||
|     contract Derived2 is Base2, Base1 { | ||||
|         constructor() public Base2() Base1() {} | ||||
|         constructor() Base2() Base1() {} | ||||
|     } | ||||
| 
 | ||||
|     // Constructors are still executed in the following order: | ||||
| @ -542,7 +554,7 @@ One area where inheritance linearization is especially important and perhaps not | ||||
|     //  2 - Base1 | ||||
|     //  3 - Derived3 | ||||
|     contract Derived3 is Base2, Base1 { | ||||
|         constructor() public Base1() Base2() {} | ||||
|         constructor() Base1() Base2() {} | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -23,7 +23,7 @@ Interfaces are denoted by their own keyword: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >=0.6.2 <0.8.0; | ||||
| 
 | ||||
|     interface Token { | ||||
|         enum TokenType { Fungible, NonFungible } | ||||
| @ -44,7 +44,7 @@ inheritance. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >=0.6.2 <0.8.0; | ||||
| 
 | ||||
|     interface ParentA { | ||||
|         function test() external returns (uint256); | ||||
|  | ||||
| @ -30,9 +30,8 @@ not possible to destroy a library. | ||||
| Libraries can be seen as implicit base contracts of the contracts that use them. | ||||
| They will not be explicitly visible in the inheritance hierarchy, but calls | ||||
| to library functions look just like calls to functions of explicit base | ||||
| contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, | ||||
| ``internal`` functions of libraries are visible in all contracts, just as | ||||
| if the library were a base contract. Of course, calls to internal functions | ||||
| contracts (using qualified access like ``L.f()``). | ||||
| Of course, calls to internal functions | ||||
| use the internal calling convention, which means that all internal types | ||||
| can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied. | ||||
| To realize this in the EVM, code of internal library functions | ||||
| @ -48,7 +47,7 @@ more advanced example to implement a set). | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     // We define a new struct datatype that will be used to | ||||
| @ -127,7 +126,7 @@ custom types without the overhead of external function calls: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     struct bigint { | ||||
|         uint[] limbs; | ||||
| @ -242,7 +241,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.14 <0.7.0; | ||||
|     pragma solidity >=0.5.14 <0.8.0; | ||||
| 
 | ||||
|     library L { | ||||
|         function f(uint256) external {} | ||||
|  | ||||
| @ -30,7 +30,7 @@ Let us rewrite the set example from the | ||||
| :ref:`libraries` in this way:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     // This is the same code as before, just without comments | ||||
| @ -83,7 +83,7 @@ Let us rewrite the set example from the | ||||
| It is also possible to extend elementary types in that way:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     library Search { | ||||
|         function indexOf(uint[] storage self, uint value) | ||||
|  | ||||
| @ -55,7 +55,7 @@ return parameter list for functions. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f(uint a) private pure returns (uint b) { return a + 1; } | ||||
| @ -70,7 +70,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint private data; | ||||
| @ -115,7 +115,7 @@ when they are declared. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint public data = 42; | ||||
| @ -136,7 +136,7 @@ it evaluates to a state variable.  If it is accessed externally | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint public data; | ||||
| @ -156,7 +156,7 @@ to write a function, for example: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract arrayExample { | ||||
|         // public state variable | ||||
| @ -183,7 +183,7 @@ The next example is more complex: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract Complex { | ||||
|         struct Data { | ||||
|  | ||||
| @ -27,10 +27,10 @@ Team Calls | ||||
| If you have issues or pull requests to discuss, or are interested in hearing what | ||||
| the team and contributors are working on, you can join our public team calls: | ||||
| 
 | ||||
| - Monday at 12pm CET | ||||
| - Wednesday at 3pm CET | ||||
| - Mondays at 12pm CET/CEST | ||||
| - Wednesdays at 2pm CET/CEST | ||||
| 
 | ||||
| Both calls take place on `Google Hangouts <https://hangouts.google.com/hangouts/_/ethereum.org/solidity-weekly>`_. | ||||
| Both calls take place on `Google Meet <https://meet.google.com/mrq-kbwv-edg>`_. | ||||
| 
 | ||||
| How to Report Issues | ||||
| ==================== | ||||
| @ -443,7 +443,7 @@ or ``interface`` using the ``./test/cmdlineTests.sh`` script when you create a P | ||||
| ensure they work and pass tests before creating the PR. | ||||
| 
 | ||||
| Ensure that all code examples begin with a ``pragma`` version that spans the largest where the contract code is valid. | ||||
| For example ``pragma solidity >=0.4.0 <0.7.0;``. | ||||
| For example ``pragma solidity >=0.4.0 <0.8.0;``. | ||||
| 
 | ||||
| Running Documentation Tests | ||||
| --------------------------- | ||||
|  | ||||
| @ -42,7 +42,7 @@ Functions of the current contract can be called directly ("internally"), also re | ||||
| this nonsensical example:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function g(uint a) public pure returns (uint ret) { return a + f(); } | ||||
| @ -84,7 +84,7 @@ to the total balance of that contract: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >=0.6.2 <0.8.0; | ||||
| 
 | ||||
|     contract InfoFeed { | ||||
|         function info() public payable returns (uint ret) { return 42; } | ||||
| @ -125,9 +125,9 @@ throws an exception or goes out of gas. | ||||
|     so your contract is not vulnerable to a reentrancy exploit. | ||||
| 
 | ||||
| .. note:: | ||||
|     Before Solidity 0.6.2, the recommended way to specify the value and gas | ||||
|     was to use ``f.value(x).gas(g)()``. This is still possible but deprecated | ||||
|     and will be removed with Solidity 0.7.0. | ||||
|     Before Solidity 0.6.2, the recommended way to specify the value and gas was to | ||||
|     use ``f.value(x).gas(g)()``. This was deprecated in Solidity 0.6.2 and is no | ||||
|     longer possible since Solidity 0.7.0. | ||||
| 
 | ||||
| Named Calls and Anonymous Function Parameters | ||||
| --------------------------------------------- | ||||
| @ -140,7 +140,7 @@ parameters from the function declaration, but can be in arbitrary order. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         mapping(uint => uint) data; | ||||
| @ -164,7 +164,7 @@ Those parameters will still be present on the stack, but they are inaccessible. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         // omitted name for parameter | ||||
| @ -188,11 +188,11 @@ is compiled so recursive creation-dependencies are not possible. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract D { | ||||
|         uint public x; | ||||
|         constructor(uint a) public payable { | ||||
|         constructor(uint a) payable { | ||||
|             x = a; | ||||
|         } | ||||
|     } | ||||
| @ -244,11 +244,11 @@ which only need to be created if there is a dispute. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract D { | ||||
|         uint public x; | ||||
|         constructor(uint a) public { | ||||
|         constructor(uint a) { | ||||
|             x = a; | ||||
|         } | ||||
|     } | ||||
| @ -314,7 +314,7 @@ groupings of expressions. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint index; | ||||
| @ -360,7 +360,7 @@ because only a reference and not a copy is passed. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         uint[20] x; | ||||
| @ -419,7 +419,7 @@ the two variables have the same name but disjoint scopes. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
|     contract C { | ||||
|         function minimalScoping() pure public { | ||||
|             { | ||||
| @ -441,7 +441,7 @@ In any case, you will get a warning about the outer variable being shadowed. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
|     // This will report a warning | ||||
|     contract C { | ||||
|         function f() pure public returns (uint) { | ||||
| @ -463,7 +463,7 @@ In any case, you will get a warning about the outer variable being shadowed. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
|     // This will not compile | ||||
|     contract C { | ||||
|         function f() pure public returns (uint) { | ||||
| @ -552,7 +552,7 @@ and ``assert`` for internal error checking. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     contract Sharer { | ||||
|         function sendHalf(address payable addr) public payable returns (uint balance) { | ||||
| @ -597,7 +597,7 @@ The following example shows how to use an error string together with ``revert`` | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     contract VendingMachine { | ||||
|         function buy(uint amount) public payable { | ||||
| @ -641,7 +641,7 @@ A failure in an external call can be caught using a try/catch statement, as foll | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     interface DataFeed { function getData(address token) external returns (uint value); } | ||||
| 
 | ||||
|  | ||||
| @ -25,7 +25,7 @@ to receive their money - contracts cannot activate themselves. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract SimpleAuction { | ||||
|         // Parameters of the auction. Times are either | ||||
| @ -60,9 +60,9 @@ to receive their money - contracts cannot activate themselves. | ||||
|         constructor( | ||||
|             uint _biddingTime, | ||||
|             address payable _beneficiary | ||||
|         ) public { | ||||
|         ) { | ||||
|             beneficiary = _beneficiary; | ||||
|             auctionEndTime = now + _biddingTime; | ||||
|             auctionEndTime = block.timestamp + _biddingTime; | ||||
|         } | ||||
| 
 | ||||
|         /// Bid on the auction with the value sent | ||||
| @ -79,7 +79,7 @@ to receive their money - contracts cannot activate themselves. | ||||
|             // Revert the call if the bidding | ||||
|             // period is over. | ||||
|             require( | ||||
|                 now <= auctionEndTime, | ||||
|                 block.timestamp <= auctionEndTime, | ||||
|                 "Auction already ended." | ||||
|             ); | ||||
| 
 | ||||
| @ -141,7 +141,7 @@ to receive their money - contracts cannot activate themselves. | ||||
|             // external contracts. | ||||
| 
 | ||||
|             // 1. Conditions | ||||
|             require(now >= auctionEndTime, "Auction not yet ended."); | ||||
|             require(block.timestamp >= auctionEndTime, "Auction not yet ended."); | ||||
|             require(!ended, "auctionEnd has already been called."); | ||||
| 
 | ||||
|             // 2. Effects | ||||
| @ -186,7 +186,7 @@ invalid bids. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract BlindAuction { | ||||
|         struct Bid { | ||||
| @ -213,16 +213,16 @@ invalid bids. | ||||
|         /// functions. `onlyBefore` is applied to `bid` below: | ||||
|         /// The new function body is the modifier's body where | ||||
|         /// `_` is replaced by the old function body. | ||||
|         modifier onlyBefore(uint _time) { require(now < _time); _; } | ||||
|         modifier onlyAfter(uint _time) { require(now > _time); _; } | ||||
|         modifier onlyBefore(uint _time) { require(block.timestamp < _time); _; } | ||||
|         modifier onlyAfter(uint _time) { require(block.timestamp > _time); _; } | ||||
| 
 | ||||
|         constructor( | ||||
|             uint _biddingTime, | ||||
|             uint _revealTime, | ||||
|             address payable _beneficiary | ||||
|         ) public { | ||||
|         ) { | ||||
|             beneficiary = _beneficiary; | ||||
|             biddingEnd = now + _biddingTime; | ||||
|             biddingEnd = block.timestamp + _biddingTime; | ||||
|             revealEnd = biddingEnd + _revealTime; | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -143,14 +143,14 @@ The full contract | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.24 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract ReceiverPays { | ||||
|         address owner = msg.sender; | ||||
| 
 | ||||
|         mapping(uint256 => bool) usedNonces; | ||||
| 
 | ||||
|         constructor() public payable {} | ||||
|         constructor() payable {} | ||||
| 
 | ||||
|         function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public { | ||||
|             require(!usedNonces[nonce]); | ||||
| @ -340,7 +340,7 @@ The full contract | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract SimplePaymentChannel { | ||||
|         address payable public sender;      // The account sending payments. | ||||
| @ -348,12 +348,11 @@ The full contract | ||||
|         uint256 public expiration;  // Timeout in case the recipient never closes. | ||||
| 
 | ||||
|         constructor (address payable _recipient, uint256 duration) | ||||
|             public | ||||
|             payable | ||||
|         { | ||||
|             sender = msg.sender; | ||||
|             recipient = _recipient; | ||||
|             expiration = now + duration; | ||||
|             expiration = block.timestamp + duration; | ||||
|         } | ||||
| 
 | ||||
|         /// the recipient can close the channel at any time by presenting a | ||||
| @ -378,7 +377,7 @@ The full contract | ||||
|         /// if the timeout is reached without the recipient closing the channel, | ||||
|         /// then the Ether is released back to the sender. | ||||
|         function claimTimeout() public { | ||||
|             require(now >= expiration); | ||||
|             require(block.timestamp >= expiration); | ||||
|             selfdestruct(sender); | ||||
|         } | ||||
| 
 | ||||
|  | ||||
| @ -20,7 +20,7 @@ and the sum of all balances is an invariant across the lifetime of the contract. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     library Balances { | ||||
|         function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { | ||||
|  | ||||
| @ -26,7 +26,7 @@ you can use state machine-like constructs inside a contract. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract Purchase { | ||||
|         uint public value; | ||||
| @ -74,7 +74,7 @@ you can use state machine-like constructs inside a contract. | ||||
|         // Ensure that `msg.value` is an even number. | ||||
|         // Division will truncate if it is an odd number. | ||||
|         // Check via multiplication that it wasn't an odd number. | ||||
|         constructor() public payable { | ||||
|         constructor() payable { | ||||
|             seller = msg.sender; | ||||
|             value = msg.value / 2; | ||||
|             require((2 * value) == msg.value, "Value has to be even."); | ||||
|  | ||||
| @ -33,7 +33,7 @@ of votes. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     /// @title Voting with delegation. | ||||
|     contract Ballot { | ||||
| @ -63,7 +63,7 @@ of votes. | ||||
|         Proposal[] public proposals; | ||||
| 
 | ||||
|         /// Create a new ballot to choose one of `proposalNames`. | ||||
|         constructor(bytes32[] memory proposalNames) public { | ||||
|         constructor(bytes32[] memory proposalNames) { | ||||
|             chairperson = msg.sender; | ||||
|             voters[chairperson].weight = 1; | ||||
| 
 | ||||
|  | ||||
| @ -127,6 +127,7 @@ Contents | ||||
| 
 | ||||
|    050-breaking-changes.rst | ||||
|    060-breaking-changes.rst | ||||
|    070-breaking-changes.rst | ||||
|    natspec-format.rst | ||||
|    security-considerations.rst | ||||
|    resources.rst | ||||
| @ -136,3 +137,4 @@ Contents | ||||
|    common-patterns.rst | ||||
|    bugs.rst | ||||
|    contributing.rst | ||||
|    brand-guide.rst | ||||
|  | ||||
| @ -72,7 +72,7 @@ the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25 | ||||
| 
 | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     contract C { | ||||
| @ -173,7 +173,7 @@ value and reference types, types that are encoded packed, and nested types. | ||||
| .. code:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
|     contract A { | ||||
|         struct S { | ||||
|             uint128 a; | ||||
|  | ||||
| @ -18,7 +18,7 @@ Storage Example | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract SimpleStorage { | ||||
|         uint storedData; | ||||
| @ -83,7 +83,7 @@ registering with a username and password, all you need is an Ethereum keypair. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.5.99 <0.8.0; | ||||
| 
 | ||||
|     contract Coin { | ||||
|         // The keyword "public" makes variables | ||||
| @ -97,7 +97,7 @@ registering with a username and password, all you need is an Ethereum keypair. | ||||
| 
 | ||||
|         // Constructor code is only run when the contract | ||||
|         // is created | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             minter = msg.sender; | ||||
|         } | ||||
| 
 | ||||
| @ -186,7 +186,7 @@ and any user interface calls the automatically generated ``balances`` function f | ||||
| 
 | ||||
| .. index:: coin | ||||
| 
 | ||||
| The :ref:`constructor<constructor>` is a special function run during the creation of the contract and | ||||
| The :ref:`constructor<constructor>` is a special function that is executed during the creation of the contract and | ||||
| cannot be called afterwards. In this case, it permanently stores the address of the person creating the | ||||
| contract. The ``msg`` variable (together with ``tx`` and ``block``) is a | ||||
| :ref:`special global variable <special-variables-functions>` that | ||||
|  | ||||
| @ -317,7 +317,7 @@ for the two function parameters and two return variables. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.21 <0.7.0; | ||||
|     pragma solidity >=0.4.21 <0.8.0; | ||||
| 
 | ||||
|     /** @title Shape calculator. */ | ||||
|     contract ShapeCalculator { | ||||
|  | ||||
| @ -49,7 +49,7 @@ The following example shows a contract and a function using all available tags. | ||||
| .. code:: solidity | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >0.6.10 <0.7.0; | ||||
|     pragma solidity >0.6.10 <0.8.0; | ||||
| 
 | ||||
|     /// @title A simulator for trees | ||||
|     /// @author Larry A. Gardner | ||||
|  | ||||
| @ -59,7 +59,7 @@ complete contract): | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     // THIS CONTRACT CONTAINS A BUG - DO NOT USE | ||||
|     contract Fund { | ||||
| @ -83,7 +83,7 @@ as it uses ``call`` which forwards all remaining gas by default: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.2 <0.7.0; | ||||
|     pragma solidity >=0.6.2 <0.8.0; | ||||
| 
 | ||||
|     // THIS CONTRACT CONTAINS A BUG - DO NOT USE | ||||
|     contract Fund { | ||||
| @ -103,7 +103,7 @@ outlined further below: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.11 <0.7.0; | ||||
|     pragma solidity >=0.4.11 <0.8.0; | ||||
| 
 | ||||
|     contract Fund { | ||||
|         /// @dev Mapping of ether shares of the contract. | ||||
| @ -201,13 +201,13 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     // THIS CONTRACT CONTAINS A BUG - DO NOT USE | ||||
|     contract TxUserWallet { | ||||
|         address owner; | ||||
| 
 | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             owner = msg.sender; | ||||
|         } | ||||
| 
 | ||||
| @ -222,7 +222,7 @@ Now someone tricks you into sending Ether to the address of this attack wallet: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     interface TxUserWallet { | ||||
|         function transferTo(address payable dest, uint amount) external; | ||||
| @ -231,7 +231,7 @@ Now someone tricks you into sending Ether to the address of this attack wallet: | ||||
|     contract TxAttackWallet { | ||||
|         address payable owner; | ||||
| 
 | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             owner = msg.sender; | ||||
|         } | ||||
| 
 | ||||
| @ -283,7 +283,7 @@ field of a ``struct`` that is the base type of a dynamic storage array.  The | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract Map { | ||||
|         mapping (uint => uint)[] array; | ||||
|  | ||||
| @ -27,7 +27,7 @@ storage. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract SimpleStorage { | ||||
|         uint storedData; // State variable | ||||
| @ -48,7 +48,7 @@ Functions are the executable units of code within a contract. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract SimpleAuction { | ||||
|         function bid() public payable { // Function | ||||
| @ -77,7 +77,7 @@ Like functions, modifiers can be :ref:`overridden <modifier-overriding>`. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract Purchase { | ||||
|         address public seller; | ||||
| @ -105,7 +105,7 @@ Events are convenience interfaces with the EVM logging facilities. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.21 <0.7.0; | ||||
|     pragma solidity >=0.4.21 <0.8.0; | ||||
| 
 | ||||
|     contract SimpleAuction { | ||||
|         event HighestBidIncreased(address bidder, uint amount); // Event | ||||
| @ -130,7 +130,7 @@ Structs are custom defined types that can group several variables (see | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract Ballot { | ||||
|         struct Voter { // Struct | ||||
| @ -152,7 +152,7 @@ Enums can be used to create custom types with a finite set of 'constant values' | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract Purchase { | ||||
|         enum State { Created, Locked, Inactive } // Enum | ||||
|  | ||||
| @ -56,7 +56,7 @@ Surround top level declarations in solidity source with two blank lines. | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|         // ... | ||||
| @ -75,7 +75,7 @@ Yes:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|         // ... | ||||
| @ -95,7 +95,7 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     abstract contract A { | ||||
|         function spam() public virtual pure; | ||||
| @ -116,7 +116,7 @@ Yes:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     abstract contract A { | ||||
|         function spam() virtual pure public; | ||||
| @ -251,7 +251,7 @@ Import statements should always be placed at the top of the file. | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     import "./Owned.sol"; | ||||
| 
 | ||||
| @ -266,7 +266,7 @@ Yes:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|         // ... | ||||
| @ -300,10 +300,10 @@ Within a grouping, place the ``view`` and ``pure`` functions last. | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             // ... | ||||
|         } | ||||
| 
 | ||||
| @ -337,7 +337,7 @@ Yes:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity ^0.6.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract A { | ||||
| 
 | ||||
| @ -357,7 +357,7 @@ No:: | ||||
|         // Public functions | ||||
|         // ... | ||||
| 
 | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             // ... | ||||
|         } | ||||
| 
 | ||||
| @ -445,7 +445,7 @@ should: | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract Coin { | ||||
|         struct Bank { | ||||
| @ -457,7 +457,7 @@ Yes:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract Coin | ||||
|     { | ||||
| @ -758,19 +758,19 @@ manner as modifiers if the function declaration is long or hard to read. | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     // Base contracts just to make this compile | ||||
|     contract B { | ||||
|         constructor(uint) public { | ||||
|         constructor(uint) { | ||||
|         } | ||||
|     } | ||||
|     contract C { | ||||
|         constructor(uint, uint) public { | ||||
|         constructor(uint, uint) { | ||||
|         } | ||||
|     } | ||||
|     contract D { | ||||
|         constructor(uint) public { | ||||
|         constructor(uint) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -781,7 +781,6 @@ Yes:: | ||||
|             B(param1) | ||||
|             C(param2, param3) | ||||
|             D(param4) | ||||
|             public | ||||
|         { | ||||
|             // do something with param5 | ||||
|             x = param5; | ||||
| @ -791,24 +790,24 @@ Yes:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     // Base contracts just to make this compile | ||||
|     contract B { | ||||
|         constructor(uint) public { | ||||
|         constructor(uint) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     contract C { | ||||
|         constructor(uint, uint) public { | ||||
|         constructor(uint, uint) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     contract D { | ||||
|         constructor(uint) public { | ||||
|         constructor(uint) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -819,8 +818,7 @@ No:: | ||||
|         constructor(uint param1, uint param2, uint param3, uint param4, uint param5) | ||||
|         B(param1) | ||||
|         C(param2, param3) | ||||
|         D(param4) | ||||
|         public { | ||||
|         D(param4) { | ||||
|             x = param5; | ||||
|         } | ||||
|     } | ||||
| @ -832,8 +830,7 @@ No:: | ||||
|         constructor(uint param1, uint param2, uint param3, uint param4, uint param5) | ||||
|             B(param1) | ||||
|             C(param2, param3) | ||||
|             D(param4) | ||||
|             public { | ||||
|             D(param4) { | ||||
|                 x = param5; | ||||
|             } | ||||
|     } | ||||
| @ -1015,14 +1012,14 @@ As shown in the example below, if the contract name is ``Congress`` and the libr | ||||
| Yes:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     // Owned.sol | ||||
|     contract Owned { | ||||
|         address public owner; | ||||
| 
 | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             owner = msg.sender; | ||||
|         } | ||||
| 
 | ||||
| @ -1039,7 +1036,7 @@ Yes:: | ||||
| and in ``Congress.sol``:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     import "./Owned.sol"; | ||||
| 
 | ||||
| @ -1051,14 +1048,14 @@ and in ``Congress.sol``:: | ||||
| No:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     // owned.sol | ||||
|     contract owned { | ||||
|         address public owner; | ||||
| 
 | ||||
|         constructor() public { | ||||
|         constructor() { | ||||
|             owner = msg.sender; | ||||
|         } | ||||
| 
 | ||||
| @ -1096,7 +1093,7 @@ Events should be named using the CapWords style. Examples: ``Deposit``, ``Transf | ||||
| Function Names | ||||
| ============== | ||||
| 
 | ||||
| Functions other than constructors should use mixedCase. Examples: ``getBalance``, ``transfer``, ``verifyOwner``, ``addMember``, ``changeOwner``. | ||||
| Functions should use mixedCase. Examples: ``getBalance``, ``transfer``, ``verifyOwner``, ``addMember``, ``changeOwner``. | ||||
| 
 | ||||
| 
 | ||||
| Function Argument Names | ||||
| @ -1156,7 +1153,7 @@ For example, the contract from `a simple smart contract <simple-smart-contract>` | ||||
| added looks like the one below:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     /// @author The Solidity Team | ||||
|  | ||||
| @ -26,6 +26,7 @@ are allowed for state variables, as storage reference types | ||||
| in functions, or as parameters for library functions. | ||||
| They cannot be used as parameters or return parameters | ||||
| of contract functions that are publicly visible. | ||||
| These restrictions are also true for arrays and structs that contain mappings. | ||||
| 
 | ||||
| You can mark state variables of mapping type as ``public`` and Solidity creates a | ||||
| :ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter. | ||||
| @ -42,7 +43,7 @@ contract that returns the value at the specified address. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract MappingExample { | ||||
|         mapping(address => uint) public balances; | ||||
| @ -68,7 +69,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
|     contract MappingExample { | ||||
| 
 | ||||
| @ -123,7 +124,7 @@ the ``sum`` function iterates over to sum all the values. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     struct IndexValue { uint keyIndex; uint value; } | ||||
|     struct KeyFlag { uint key; bool deleted; } | ||||
|  | ||||
| @ -43,7 +43,7 @@ value it referred to previously. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract DeleteExample { | ||||
|         uint data; | ||||
|  | ||||
| @ -63,7 +63,7 @@ Data locations are not only relevant for persistency of data, but also for the s | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.5.0 <0.7.0; | ||||
|     pragma solidity >=0.5.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         // The data location of x is storage. | ||||
| @ -174,7 +174,7 @@ or create a new memory array and copy every element. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f(uint len) public pure { | ||||
| @ -206,7 +206,7 @@ the first element to ``uint``. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f() public pure { | ||||
| @ -223,7 +223,7 @@ memory arrays, i.e. the following is not possible: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     // This will not compile. | ||||
|     contract C { | ||||
| @ -243,7 +243,7 @@ individual elements: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.0 <0.7.0; | ||||
|     pragma solidity >=0.4.0 <0.8.0; | ||||
| 
 | ||||
|     contract C { | ||||
|         function f() public pure { | ||||
| @ -301,7 +301,7 @@ Array Members | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     contract ArrayContract { | ||||
|         uint[2**20] m_aLotOfIntegers; | ||||
| @ -434,13 +434,13 @@ Array slices are useful to ABI-decode secondary data passed in function paramete | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
| 
 | ||||
|     contract Proxy { | ||||
|         /// @dev Address of the client contract managed by proxy i.e., this contract | ||||
|         address client; | ||||
| 
 | ||||
|         constructor(address _client) public { | ||||
|         constructor(address _client) { | ||||
|             client = _client; | ||||
|         } | ||||
| 
 | ||||
| @ -478,7 +478,7 @@ shown in the following example: | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     pragma solidity >=0.6.0 <0.8.0; | ||||
| 
 | ||||
|     // Defines a new type with two fields. | ||||
|     // Declaring a struct outside of a contract allows | ||||
| @ -505,12 +505,11 @@ shown in the following example: | ||||
| 
 | ||||
|         function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { | ||||
|             campaignID = numCampaigns++; // campaignID is return variable | ||||
|             // Creates new struct in memory and copies it to storage. | ||||
|             // We leave out the mapping type, because it is not valid in memory. | ||||
|             // If structs are copied (even from storage to storage), | ||||
|             // types that are not valid outside of storage (ex. mappings and array of mappings) | ||||
|             // are always omitted, because they cannot be enumerated. | ||||
|             campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); | ||||
|             // We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)" | ||||
|             // because the RHS creates a memory-struct "Campaign" that contains a mapping. | ||||
|             Campaign storage c = campaigns[campaignID]; | ||||
|             c.beneficiary = beneficiary; | ||||
|             c.fundingGoal = goal; | ||||
|         } | ||||
| 
 | ||||
|         function contribute(uint campaignID) public payable { | ||||
|  | ||||
| @ -64,11 +64,11 @@ Shifts | ||||
| ^^^^^^ | ||||
| 
 | ||||
| The result of a shift operation has the type of the left operand, truncating the result to match the type. | ||||
| Right operand must be unsigned type. Trying to shift by signed type will produce a compilation error. | ||||
| 
 | ||||
| - For positive and negative ``x`` values, ``x << y`` is equivalent to ``x * 2**y``. | ||||
| - For positive ``x`` values,  ``x >> y`` is equivalent to ``x / 2**y``. | ||||
| - For negative ``x`` values, ``x >> y`` is equivalent to ``(x + 1) / 2**y - 1`` (which is the same as dividing ``x`` by ``2**y`` while rounding down towards negative infinity). | ||||
| - In all cases, shifting by a negative ``y`` throws a runtime exception. | ||||
| 
 | ||||
| .. warning:: | ||||
|     Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``, | ||||
| @ -370,9 +370,9 @@ Operators: | ||||
| * Shift operators: ``<<`` (left shift), ``>>`` (right shift) | ||||
| * Index access: If ``x`` is of type ``bytesI``, then ``x[k]`` for ``0 <= k < I`` returns the ``k`` th byte (read-only). | ||||
| 
 | ||||
| The shifting operator works with any integer type as right operand (but | ||||
| The shifting operator works with unsigned integer type as right operand (but | ||||
| returns the type of the left operand), which denotes the number of bits to shift by. | ||||
| Shifting by a negative amount causes a runtime exception. | ||||
| Shifting by a signed type will produce a compilation error. | ||||
| 
 | ||||
| Members: | ||||
| 
 | ||||
| @ -444,6 +444,11 @@ long as the operands are integers. If any of the two is fractional, bit operatio | ||||
| and exponentiation is disallowed if the exponent is fractional (because that might result in | ||||
| a non-rational number). | ||||
| 
 | ||||
| Shifts and exponentiation with literal numbers as left (or base) operand and integer types | ||||
| as the right (exponent) operand are always performed | ||||
| in the ``uint256`` (for non-negative literals) or ``int256`` (for a negative literals) type, | ||||
| regardless of the type of the right (exponent) operand. | ||||
| 
 | ||||
| .. warning:: | ||||
|     Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. | ||||
| 
 | ||||
| @ -479,7 +484,9 @@ String literals are written with either double or single-quotes (``"foo"`` or `` | ||||
| 
 | ||||
| For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. | ||||
| 
 | ||||
| String literals support the following escape characters: | ||||
| String literals can only contain printable ASCII characters, which means the characters between and including 0x1F .. 0x7E. | ||||
| 
 | ||||
| Additionally, string literals also support the following escape characters: | ||||
| 
 | ||||
|  - ``\<newline>`` (escapes an actual newline) | ||||
|  - ``\\`` (backslash) | ||||
| @ -506,9 +513,19 @@ character sequence ``abcdef``. | ||||
|     "\n\"\'\\abc\ | ||||
|     def" | ||||
| 
 | ||||
| Any unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to | ||||
| Any Unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to | ||||
| terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. | ||||
| 
 | ||||
| Unicode Literals | ||||
| ---------------- | ||||
| 
 | ||||
| While regular string literals can only contain ASCII, Unicode literals – prefixed with the keyword ``unicode`` – can contain any valid UTF-8 sequence. | ||||
| They also support the very same escape sequences as regular string literals. | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     string memory a = unicode"Hello 😃"; | ||||
| 
 | ||||
| .. index:: literal, bytes | ||||
| 
 | ||||
| Hexadecimal Literals | ||||
| @ -544,7 +561,7 @@ subsequent unsigned integer values starting from ``0``. | ||||
| :: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     contract test { | ||||
|         enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } | ||||
| @ -645,18 +662,19 @@ External (or public) functions have the following members: | ||||
| 
 | ||||
| * ``.address`` returns the address of the contract of the function. | ||||
| * ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>` | ||||
| * ``.gas(uint)`` returns a callable function object which, when called, will send | ||||
|   the specified amount of gas to the target function. Deprecated - use ``{gas: ...}`` instead. | ||||
|   See :ref:`External Function Calls <external-function-calls>` for more information. | ||||
| * ``.value(uint)`` returns a callable function object which, when called, will | ||||
|   send the specified amount of wei to the target function. Deprecated - use ``{value: ...}`` instead. | ||||
|   See :ref:`External Function Calls <external-function-calls>` for more information. | ||||
| 
 | ||||
| .. note:: | ||||
|   External (or public) functions used to have the additional members | ||||
|   ``.gas(uint)`` and ``.value(uint)``. These were deprecated in Solidity 0.6.2 | ||||
|   and removed in Solidity 0.7.0. Instead use ``{gas: ...}`` and ``{value: ...}`` | ||||
|   to specify the amount of gas or the amount of wei sent to a function, | ||||
|   respectively. See :ref:`External Function Calls <external-function-calls>` for | ||||
|   more information. | ||||
| 
 | ||||
| Example that shows how to use the members:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
|     // This will report a warning | ||||
|     pragma solidity >=0.6.4 <0.8.0; | ||||
| 
 | ||||
|     contract Example { | ||||
|         function f() public payable returns (bytes4) { | ||||
| @ -665,16 +683,14 @@ Example that shows how to use the members:: | ||||
|         } | ||||
| 
 | ||||
|         function g() public { | ||||
|             this.f.gas(10).value(800)(); | ||||
|             // New syntax: | ||||
|             // this.f{gas: 10, value: 800}() | ||||
|             this.f{gas: 10, value: 800}(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| Example that shows how to use internal function types:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.16 <0.7.0; | ||||
|     pragma solidity >=0.4.16 <0.8.0; | ||||
| 
 | ||||
|     library ArrayUtils { | ||||
|         // internal functions can be used in internal library functions because | ||||
| @ -732,7 +748,7 @@ Example that shows how to use internal function types:: | ||||
| Another example that uses external function types:: | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.4.22 <0.7.0; | ||||
|     pragma solidity >=0.4.22 <0.8.0; | ||||
| 
 | ||||
| 
 | ||||
|     contract Oracle { | ||||
|  | ||||
| @ -2,23 +2,23 @@ | ||||
| Units and Globally Available Variables | ||||
| ************************************** | ||||
| 
 | ||||
| .. index:: wei, finney, szabo, ether | ||||
| .. index:: wei, finney, szabo, gwei, ether | ||||
| 
 | ||||
| Ether Units | ||||
| =========== | ||||
| 
 | ||||
| A literal number can take a suffix of ``wei``, ``gwei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. | ||||
| A literal number can take a suffix of ``wei``, ``gwei`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|     assert(1 wei == 1); | ||||
|     assert(1 gwei == 1e9); | ||||
|     assert(1 szabo == 1e12); | ||||
|     assert(1 finney == 1e15); | ||||
|     assert(1 ether == 1e18); | ||||
| 
 | ||||
| The only effect of the subdenomination suffix is a multiplication by a power of ten. | ||||
| 
 | ||||
| .. note:: | ||||
|     The denominations ``finney`` and ``szabo`` have been removed in version 0.7.0. | ||||
| 
 | ||||
| .. index:: time, seconds, minutes, hours, days, weeks, years | ||||
| 
 | ||||
| @ -48,7 +48,7 @@ These suffixes cannot be applied to variables. For example, if you want to | ||||
| interpret a function parameter in days, you can in the following way:: | ||||
| 
 | ||||
|     function f(uint start, uint daysAfter) public { | ||||
|         if (now >= start + daysAfter * 1 days) { | ||||
|         if (block.timestamp >= start + daysAfter * 1 days) { | ||||
|           // ... | ||||
|         } | ||||
|     } | ||||
| @ -62,7 +62,7 @@ There are special variables and functions which always exist in the global | ||||
| namespace and are mainly used to provide information about the blockchain | ||||
| or are general-use utility functions. | ||||
| 
 | ||||
| .. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin | ||||
| .. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin | ||||
| 
 | ||||
| 
 | ||||
| Block and Transaction Properties | ||||
| @ -79,7 +79,6 @@ Block and Transaction Properties | ||||
| - ``msg.sender`` (``address payable``): sender of the message (current call) | ||||
| - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) | ||||
| - ``msg.value`` (``uint``): number of wei sent with the message | ||||
| - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) | ||||
| - ``tx.gasprice`` (``uint``): gas price of the transaction | ||||
| - ``tx.origin`` (``address payable``): sender of the transaction (full call chain) | ||||
| 
 | ||||
| @ -89,7 +88,7 @@ Block and Transaction Properties | ||||
|     This includes calls to library functions. | ||||
| 
 | ||||
| .. note:: | ||||
|     Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, | ||||
|     Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness, | ||||
|     unless you know what you are doing. | ||||
| 
 | ||||
|     Both the timestamp and the block hash can be influenced by miners to some degree. | ||||
| @ -113,6 +112,9 @@ Block and Transaction Properties | ||||
|     The function ``gasleft`` was previously known as ``msg.gas``, which was deprecated in | ||||
|     version 0.4.21 and removed in version 0.5.0. | ||||
| 
 | ||||
| .. note:: | ||||
|     In version 0.7.0, the alias ``now`` (for ``block.timestamp``) was removed. | ||||
| 
 | ||||
| .. index:: abi, encoding, packed | ||||
| 
 | ||||
| ABI Encoding and Decoding Functions | ||||
|  | ||||
| @ -566,27 +566,40 @@ the latest version of the compiler. | ||||
| Available upgrade modules | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| 
 | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | Module          | Version | Description                                      | | ||||
| +=================+=========+==================================================+ | ||||
| | ``constructor`` | 0.5.0   | Constructors must now be defined using the       | | ||||
| |                 |         | ``constructor`` keyword.                         | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``visibility``  | 0.5.0   | Explicit function visibility is now mandatory,   | | ||||
| |                 |         | defaults to ``public``.                          | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``abstract``    | 0.6.0   | The keyword ``abstract`` has to be used if a     | | ||||
| |                 |         | contract does not implement all its functions.   | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``virtual``     | 0.6.0   | Functions without implementation outside an      | | ||||
| |                 |         | interface have to be marked ``virtual``.         | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| | ``override``    | 0.6.0   | When overriding a function or modifier, the new  | | ||||
| |                 |         | keyword ``override`` must be used.               | | ||||
| +-----------------+---------+--------------------------------------------------+ | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | Module                     | Version | Description                                      | | ||||
| +============================+=========+==================================================+ | ||||
| | ``constructor``            | 0.5.0   | Constructors must now be defined using the       | | ||||
| |                            |         | ``constructor`` keyword.                         | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``visibility``             | 0.5.0   | Explicit function visibility is now mandatory,   | | ||||
| |                            |         | defaults to ``public``.                          | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``abstract``               | 0.6.0   | The keyword ``abstract`` has to be used if a     | | ||||
| |                            |         | contract does not implement all its functions.   | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``virtual``                | 0.6.0   | Functions without implementation outside an      | | ||||
| |                            |         | interface have to be marked ``virtual``.         | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``override``               | 0.6.0   | When overriding a function or modifier, the new  | | ||||
| |                            |         | keyword ``override`` must be used.               | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``dotsyntax``              | 0.7.0   | The following syntax is deprecated:              | | ||||
| |                            |         | ``f.gas(...)()``, ``f.value(...)()`` and         | | ||||
| |                            |         | ``(new C).value(...)()``. Replace these calls by | | ||||
| |                            |         | ``f{gas: ..., value: ...}()`` and                | | ||||
| |                            |         | ``(new C){value: ...}()``.                       | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``now``                    | 0.7.0   | The ``now`` keyword is deprecated. Use           | | ||||
| |                            |         | ``block.timestamp`` instead.                     | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| | ``constructor-visibility`` | 0.7.0   | Removes visibility of constructors.              | | ||||
| |                            |         |                                                  | | ||||
| +----------------------------+---------+--------------------------------------------------+ | ||||
| 
 | ||||
| Please read :doc:`0.5.0 release notes <050-breaking-changes>` and | ||||
| :doc:`0.6.0 release notes <060-breaking-changes>` for further details. | ||||
| Please read :doc:`0.5.0 release notes <050-breaking-changes>`, | ||||
| :doc:`0.6.0 release notes <060-breaking-changes>` and | ||||
| :doc:`0.7.0 release notes <070-breaking-changes>` for further details. | ||||
| 
 | ||||
| Synopsis | ||||
| ~~~~~~~~ | ||||
| @ -622,115 +635,88 @@ If you found a bug or if you have a feature request, please | ||||
| Example | ||||
| ~~~~~~~ | ||||
| 
 | ||||
| Assume you have the following contracts you want to update declared in ``Source.sol``: | ||||
| Assume that you have the following contract in ``Source.sol``: | ||||
| 
 | ||||
| .. code-block:: none | ||||
| .. code-block:: solidity | ||||
| 
 | ||||
|     // This will not compile after 0.5.0 | ||||
|     pragma solidity >=0.6.0 <0.6.4; | ||||
|     // This will not compile after 0.7.0 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >0.4.23 <0.5.0; | ||||
| 
 | ||||
|     contract Updateable { | ||||
|         function run() public view returns (bool); | ||||
|         function update() public; | ||||
|     contract C { | ||||
|         // FIXME: remove constructor visibility and make the contract abstract | ||||
|         constructor() internal {} | ||||
|     } | ||||
| 
 | ||||
|     contract Upgradable { | ||||
|         function run() public view returns (bool); | ||||
|         function upgrade(); | ||||
|     contract D { | ||||
|         uint time; | ||||
| 
 | ||||
|         function f() public payable { | ||||
|             // FIXME: change now to block.timestamp | ||||
|             time = now; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     contract Source is Updateable, Upgradable { | ||||
|         function Source() public {} | ||||
|     contract E { | ||||
|         D d; | ||||
| 
 | ||||
|         function run() | ||||
|             public | ||||
|             view | ||||
|             returns (bool) {} | ||||
|         // FIXME: remove constructor visibility | ||||
|         constructor() public {} | ||||
| 
 | ||||
|         function update() {} | ||||
|         function upgrade() {} | ||||
|         function g() public { | ||||
|             // FIXME: change .value(5) =>  {value: 5} | ||||
|             d.f.value(5)(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Required changes | ||||
| ^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| To bring the contracts up to date with the current Solidity version, the | ||||
| following upgrade modules have to be executed: ``constructor``, | ||||
| ``visibility``, ``abstract``, ``override`` and ``virtual``. Please read the | ||||
| documentation on :ref:`available modules <upgrade-modules>` for further details. | ||||
| The above contract will not compile starting from 0.7.0. To bring the contract up to date with the | ||||
| current Solidity version, the following upgrade modules have to be executed: | ||||
| ``constructor-visibility``, ``now`` and ``dotsyntax``. Please read the documentation on | ||||
| :ref:`available modules <upgrade-modules>` for further details. | ||||
| 
 | ||||
| 
 | ||||
| Running the upgrade | ||||
| ^^^^^^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| In this example, all modules needed to upgrade the contracts above, | ||||
| are available and all of them are activated by default. Therefore you | ||||
| do not need to specify the ``--modules`` option. | ||||
| It is recommended to explicitly specify the upgrade modules by using ``--modules`` argument. | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     $ solidity-upgrade Source.sol --dry-run | ||||
|    $ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     Running analysis (and upgrade) on given source files. | ||||
|     .............. | ||||
| 
 | ||||
|     After upgrade: | ||||
| 
 | ||||
|     Found 0 errors. | ||||
|     Found 0 upgrades. | ||||
| 
 | ||||
| The above performs a dry-ran upgrade on the given file and logs statistics after all. | ||||
| In this case, the upgrade was successful and no further adjustments are needed. | ||||
| 
 | ||||
| Finally, you can run the upgrade and also write to the source file. | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     $ solidity-upgrade Source.sol | ||||
| 
 | ||||
| .. code-block:: none | ||||
| 
 | ||||
|     Running analysis (and upgrade) on given source files. | ||||
|     .............. | ||||
| 
 | ||||
|     After upgrade: | ||||
| 
 | ||||
|     Found 0 errors. | ||||
|     Found 0 upgrades. | ||||
| 
 | ||||
| 
 | ||||
| Review changes | ||||
| ^^^^^^^^^^^^^^ | ||||
| 
 | ||||
| The command above applies all changes as shown below. Please review them carefully. | ||||
| The command above applies all changes as shown below. Please review them carefully (the pragmas will | ||||
| have to be updated manually.) | ||||
| 
 | ||||
| .. code-block:: solidity | ||||
| 
 | ||||
|     pragma solidity >0.6.99 <0.8.0; | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.6.0 <0.7.0; | ||||
| 
 | ||||
|     abstract contract Updateable { | ||||
|         function run() public view virtual returns (bool); | ||||
|         function update() public virtual; | ||||
|     abstract contract C { | ||||
|         // FIXME: remove constructor visibility and make the contract abstract | ||||
|         constructor() {} | ||||
|     } | ||||
| 
 | ||||
|     abstract contract Upgradable { | ||||
|         function run() public view virtual returns (bool); | ||||
|         function upgrade() public virtual; | ||||
|     contract D { | ||||
|         uint time; | ||||
| 
 | ||||
|         function f() public payable { | ||||
|             // FIXME: change now to block.timestamp | ||||
|             time = block.timestamp; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     contract Source is Updateable, Upgradable { | ||||
|         constructor() public {} | ||||
|     contract E { | ||||
|         D d; | ||||
| 
 | ||||
|         function run() | ||||
|             public | ||||
|             view | ||||
|             override(Updateable,Upgradable) | ||||
|             returns (bool) {} | ||||
|         // FIXME: remove constructor visibility | ||||
|         constructor() {} | ||||
| 
 | ||||
|         function update() public override {} | ||||
|         function upgrade() public override {} | ||||
|         function g() public { | ||||
|             // FIXME: change .value(5) =>  {value: 5} | ||||
|             d.f{value: 5}(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -30,6 +30,8 @@ | ||||
| #include <libevmasm/ConstantOptimiser.h> | ||||
| #include <libevmasm/GasMeter.h> | ||||
| 
 | ||||
| #include <liblangutil/Exceptions.h> | ||||
| 
 | ||||
| #include <fstream> | ||||
| #include <json/json.h> | ||||
| 
 | ||||
| @ -699,12 +701,10 @@ LinkerObject const& Assembly::assemble() const | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	assertThrow( | ||||
| 		immutableReferencesBySub.empty(), | ||||
| 		AssemblyException, | ||||
| 		"Some immutables were read from but never assigned." | ||||
| 	); | ||||
| 
 | ||||
| 	if (!immutableReferencesBySub.empty()) | ||||
| 		throw | ||||
| 			langutil::Error(1284_error, langutil::Error::Type::CodeGenerationError) << | ||||
| 			util::errinfo_comment("Some immutables were read from but never assigned, possibly because of optimization."); | ||||
| 
 | ||||
| 	if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) | ||||
| 		// Append an INVALID here to help tests find miscompilation.
 | ||||
|  | ||||
| @ -33,6 +33,9 @@ Error::Error(ErrorId _errorId, Type _type, SourceLocation const& _location, stri | ||||
| { | ||||
| 	switch (m_type) | ||||
| 	{ | ||||
| 	case Type::CodeGenerationError: | ||||
| 		m_typeName = "CodeGenerationError"; | ||||
| 		break; | ||||
| 	case Type::DeclarationError: | ||||
| 		m_typeName = "DeclarationError"; | ||||
| 		break; | ||||
|  | ||||
| @ -78,6 +78,7 @@ class Error: virtual public util::Exception | ||||
| public: | ||||
| 	enum class Type | ||||
| 	{ | ||||
| 		CodeGenerationError, | ||||
| 		DeclarationError, | ||||
| 		DocstringParsingError, | ||||
| 		ParserError, | ||||
|  | ||||
| @ -73,6 +73,7 @@ string to_string(ScannerError _errorCode) | ||||
| 		case ScannerError::IllegalHexDigit: return "Hexadecimal digit missing or invalid."; | ||||
| 		case ScannerError::IllegalCommentTerminator: return "Expected multi-line comment-terminator."; | ||||
| 		case ScannerError::IllegalEscapeSequence: return "Invalid escape sequence."; | ||||
| 		case ScannerError::IllegalCharacterInString: return "Invalid character in string."; | ||||
| 		case ScannerError::IllegalStringEndQuote: return "Expected string end-quote."; | ||||
| 		case ScannerError::IllegalNumberSeparator: return "Invalid use of number separator '_'."; | ||||
| 		case ScannerError::IllegalExponent: return "Invalid exponent."; | ||||
| @ -508,7 +509,7 @@ void Scanner::scanToken() | ||||
| 		{ | ||||
| 		case '"': | ||||
| 		case '\'': | ||||
| 			token = scanString(); | ||||
| 			token = scanString(false); | ||||
| 			break; | ||||
| 		case '<': | ||||
| 			// < <= << <<=
 | ||||
| @ -683,6 +684,18 @@ void Scanner::scanToken() | ||||
| 					else | ||||
| 						token = setError(ScannerError::IllegalToken); | ||||
| 				} | ||||
| 				else if (token == Token::Unicode) | ||||
| 				{ | ||||
| 					// reset
 | ||||
| 					m = 0; | ||||
| 					n = 0; | ||||
| 
 | ||||
| 					// Special quoted hex string must follow
 | ||||
| 					if (m_char == '"' || m_char == '\'') | ||||
| 						token = scanString(true); | ||||
| 					else | ||||
| 						token = setError(ScannerError::IllegalToken); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (isDecimalDigit(m_char)) | ||||
| 				token = scanNumber(); | ||||
| @ -774,7 +787,7 @@ bool Scanner::isUnicodeLinebreak() | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| Token Scanner::scanString() | ||||
| Token Scanner::scanString(bool const _isUnicode) | ||||
| { | ||||
| 	char const quote = m_char; | ||||
| 	advance();  // consume quote
 | ||||
| @ -789,13 +802,23 @@ Token Scanner::scanString() | ||||
| 				return setError(ScannerError::IllegalEscapeSequence); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			// Report error on non-printable characters in string literals, however
 | ||||
| 			// allow anything for unicode string literals, because their validity will
 | ||||
| 			// be verified later (in the syntax checker).
 | ||||
| 			//
 | ||||
| 			// We are using a manual range and not isprint() to avoid
 | ||||
| 			// any potential complications with locale.
 | ||||
| 			if (!_isUnicode && (static_cast<unsigned>(c) <= 0x1f || static_cast<unsigned>(c) >= 0x7f)) | ||||
| 				return setError(ScannerError::IllegalCharacterInString); | ||||
| 			addLiteralChar(c); | ||||
| 		} | ||||
| 	} | ||||
| 	if (m_char != quote) | ||||
| 		return setError(ScannerError::IllegalStringEndQuote); | ||||
| 	literal.complete(); | ||||
| 	advance();  // consume quote
 | ||||
| 	return Token::StringLiteral; | ||||
| 	return _isUnicode ? Token::UnicodeStringLiteral : Token::StringLiteral; | ||||
| } | ||||
| 
 | ||||
| Token Scanner::scanHexString() | ||||
|  | ||||
| @ -77,6 +77,7 @@ enum class ScannerError | ||||
| 	IllegalHexDigit, | ||||
| 	IllegalCommentTerminator, | ||||
| 	IllegalEscapeSequence, | ||||
| 	IllegalCharacterInString, | ||||
| 	IllegalStringEndQuote, | ||||
| 	IllegalNumberSeparator, | ||||
| 	IllegalExponent, | ||||
| @ -228,7 +229,7 @@ private: | ||||
| 	Token scanNumber(char _charSeen = 0); | ||||
| 	std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword(); | ||||
| 
 | ||||
| 	Token scanString(); | ||||
| 	Token scanString(bool const _isUnicode); | ||||
| 	Token scanHexString(); | ||||
| 	/// Scans a single line comment and returns its corrected end position.
 | ||||
| 	size_t scanSingleLineDocComment(); | ||||
|  | ||||
| @ -148,6 +148,7 @@ namespace solidity::langutil | ||||
| 	K(As, "as", 0)                                                     \ | ||||
| 	K(Assembly, "assembly", 0)                                         \ | ||||
| 	K(Break, "break", 0)                                               \ | ||||
| 	K(Catch, "catch", 0)                                               \ | ||||
| 	K(Constant, "constant", 0)                                         \ | ||||
| 	K(Constructor, "constructor", 0)                                   \ | ||||
| 	K(Continue, "continue", 0)                                         \ | ||||
| @ -187,17 +188,17 @@ namespace solidity::langutil | ||||
| 	K(CallData, "calldata", 0)                                         \ | ||||
| 	K(Struct, "struct", 0)                                             \ | ||||
| 	K(Throw, "throw", 0)                                               \ | ||||
| 	K(Try, "try", 0)                                                   \ | ||||
| 	K(Type, "type", 0)                                                 \ | ||||
| 	K(Unicode, "unicode", 0)                                           \ | ||||
| 	K(Using, "using", 0)                                               \ | ||||
| 	K(Var, "var", 0)                                                   \ | ||||
| 	K(View, "view", 0)                                                 \ | ||||
| 	K(Virtual, "virtual", 0)                                           \ | ||||
| 	K(While, "while", 0)                                               \ | ||||
| 	\ | ||||
| 	/* Ether subdenominations */                                       \ | ||||
| 	K(SubWei, "wei", 0)                                                \ | ||||
| 	K(SubSzabo, "szabo", 0)                                            \ | ||||
| 	K(SubFinney, "finney", 0)                                          \ | ||||
| 	K(SubGwei, "gwei", 0)                                              \ | ||||
| 	K(SubEther, "ether", 0)                                            \ | ||||
| 	K(SubSecond, "seconds", 0)                                         \ | ||||
| 	K(SubMinute, "minutes", 0)                                         \ | ||||
| @ -227,12 +228,12 @@ namespace solidity::langutil | ||||
| 	K(FalseLiteral, "false", 0)                                        \ | ||||
| 	T(Number, nullptr, 0)                                              \ | ||||
| 	T(StringLiteral, nullptr, 0)                                       \ | ||||
| 	T(UnicodeStringLiteral, nullptr, 0)                                \ | ||||
| 	T(HexStringLiteral, nullptr, 0)                                    \ | ||||
| 	T(CommentLiteral, nullptr, 0)                                      \ | ||||
| 	\ | ||||
| 	/* Identifiers (not keywords or future reserved words). */         \ | ||||
| 	T(Identifier, nullptr, 0)                                          \ | ||||
| 	T(SubGwei, "gwei", 0)                                              \ | ||||
| 	\ | ||||
| 	/* Keywords reserved for future use. */                            \ | ||||
| 	K(After, "after", 0)                                               \ | ||||
| @ -240,7 +241,6 @@ namespace solidity::langutil | ||||
| 	K(Apply, "apply", 0)                                               \ | ||||
| 	K(Auto, "auto", 0)                                                 \ | ||||
| 	K(Case, "case", 0)                                                 \ | ||||
| 	K(Catch, "catch", 0)                                               \ | ||||
| 	K(CopyOf, "copyof", 0)                                             \ | ||||
| 	K(Default, "default", 0)                                           \ | ||||
| 	K(Define, "define", 0)                                             \ | ||||
| @ -263,10 +263,10 @@ namespace solidity::langutil | ||||
| 	K(Static, "static", 0)                                             \ | ||||
| 	K(Supports, "supports", 0)                                         \ | ||||
| 	K(Switch, "switch", 0)                                             \ | ||||
| 	K(Try, "try", 0)                                                   \ | ||||
| 	K(Typedef, "typedef", 0)                                           \ | ||||
| 	K(TypeOf, "typeof", 0)                                             \ | ||||
| 	K(Unchecked, "unchecked", 0)                                       \ | ||||
| 	K(Var, "var", 0)                                                   \ | ||||
| 	\ | ||||
| 	/* Illegal token - not able to scan. */                            \ | ||||
| 	T(Illegal, "ILLEGAL", 0)                                           \ | ||||
| @ -307,13 +307,12 @@ namespace TokenTraits | ||||
| 	constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } | ||||
| 	constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } | ||||
| 
 | ||||
| 	constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true) | ||||
| 	constexpr bool isStateMutabilitySpecifier(Token op) | ||||
| 	{ | ||||
| 		return (op == Token::Constant && _allowConstant) | ||||
| 			|| op == Token::Pure || op == Token::View || op == Token::Payable; | ||||
| 		return op == Token::Pure || op == Token::View || op == Token::Payable; | ||||
| 	} | ||||
| 
 | ||||
| 	constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; } | ||||
| 	constexpr bool isEtherSubdenomination(Token op) { return op >= Token::SubWei && op <= Token::SubEther; } | ||||
| 	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); } | ||||
| 
 | ||||
|  | ||||
| @ -83,7 +83,7 @@ void CHCSmtLib2Interface::addRule(Expression const& _expr, std::string const& _n | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| pair<CheckResult, vector<string>> CHCSmtLib2Interface::query(Expression const& _block) | ||||
| pair<CheckResult, CHCSolverInterface::CexGraph> CHCSmtLib2Interface::query(Expression const& _block) | ||||
| { | ||||
| 	string accumulated{}; | ||||
| 	swap(m_accumulatedOutput, accumulated); | ||||
| @ -108,7 +108,7 @@ pair<CheckResult, vector<string>> CHCSmtLib2Interface::query(Expression const& _ | ||||
| 		result = CheckResult::ERROR; | ||||
| 
 | ||||
| 	// TODO collect invariants or counterexamples.
 | ||||
| 	return make_pair(result, vector<string>{}); | ||||
| 	return {result, {}}; | ||||
| } | ||||
| 
 | ||||
| void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort) | ||||
|  | ||||
| @ -43,7 +43,7 @@ public: | ||||
| 
 | ||||
| 	void addRule(Expression const& _expr, std::string const& _name) override; | ||||
| 
 | ||||
| 	std::pair<CheckResult, std::vector<std::string>> query(Expression const& _expr) override; | ||||
| 	std::pair<CheckResult, CexGraph> query(Expression const& _expr) override; | ||||
| 
 | ||||
| 	void declareVariable(std::string const& _name, SortPointer const& _sort) override; | ||||
| 
 | ||||
|  | ||||
| @ -24,6 +24,9 @@ | ||||
| 
 | ||||
| #include <libsmtutil/SolverInterface.h> | ||||
| 
 | ||||
| #include <map> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace solidity::smtutil | ||||
| { | ||||
| 
 | ||||
| @ -41,9 +44,18 @@ public: | ||||
| 	/// Needs to bound all vars as universally quantified.
 | ||||
| 	virtual void addRule(Expression const& _expr, std::string const& _name) = 0; | ||||
| 
 | ||||
| 	/// Takes a function application and checks
 | ||||
| 	/// for reachability.
 | ||||
| 	virtual std::pair<CheckResult, std::vector<std::string>> query( | ||||
| 	/// first: predicate name
 | ||||
| 	/// second: predicate arguments
 | ||||
| 	using CexNode = std::pair<std::string, std::vector<std::string>>; | ||||
| 	struct CexGraph | ||||
| 	{ | ||||
| 		std::map<unsigned, CexNode> nodes; | ||||
| 		std::map<unsigned, std::vector<unsigned>> edges; | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Takes a function application _expr and checks for reachability.
 | ||||
| 	/// @returns solving result and a counterexample graph, if possible.
 | ||||
| 	virtual std::pair<CheckResult, CexGraph> query( | ||||
| 		Expression const& _expr | ||||
| 	) = 0; | ||||
| }; | ||||
|  | ||||
| @ -20,6 +20,9 @@ | ||||
| 
 | ||||
| #include <libsolutil/CommonIO.h> | ||||
| 
 | ||||
| #include <set> | ||||
| #include <stack> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::smtutil; | ||||
| @ -33,17 +36,7 @@ Z3CHCInterface::Z3CHCInterface(): | ||||
| 	z3::set_param("rewriter.pull_cheap_ite", true); | ||||
| 	z3::set_param("rlimit", Z3Interface::resourceLimit); | ||||
| 
 | ||||
| 	// Spacer options.
 | ||||
| 	// These needs to be set in the solver.
 | ||||
| 	// https://github.com/Z3Prover/z3/blob/master/src/muz/base/fp_params.pyg
 | ||||
| 	z3::params p(*m_context); | ||||
| 	// These are useful for solving problems with arrays and loops.
 | ||||
| 	// Use quantified lemma generalizer.
 | ||||
| 	p.set("fp.spacer.q3.use_qgen", true); | ||||
| 	p.set("fp.spacer.mbqi", false); | ||||
| 	// Ground pobs by using values from a model.
 | ||||
| 	p.set("fp.spacer.ground_pobs", false); | ||||
| 	m_solver.set(p); | ||||
| 	setSpacerOptions(); | ||||
| } | ||||
| 
 | ||||
| void Z3CHCInterface::declareVariable(string const& _name, SortPointer const& _sort) | ||||
| @ -72,10 +65,10 @@ void Z3CHCInterface::addRule(Expression const& _expr, string const& _name) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pair<CheckResult, vector<string>> Z3CHCInterface::query(Expression const& _expr) | ||||
| pair<CheckResult, CHCSolverInterface::CexGraph> Z3CHCInterface::query(Expression const& _expr) | ||||
| { | ||||
| 	CheckResult result; | ||||
| 	vector<string> values; | ||||
| 	CHCSolverInterface::CexGraph cex; | ||||
| 	try | ||||
| 	{ | ||||
| 		z3::expr z3Expr = m_z3Interface->toZ3Expr(_expr); | ||||
| @ -84,8 +77,9 @@ pair<CheckResult, vector<string>> Z3CHCInterface::query(Expression const& _expr) | ||||
| 		case z3::check_result::sat: | ||||
| 		{ | ||||
| 			result = CheckResult::SATISFIABLE; | ||||
| 			// TODO retrieve model.
 | ||||
| 			break; | ||||
| 			auto proof = m_solver.get_answer(); | ||||
| 			auto cex = cexGraph(proof); | ||||
| 			return {result, cex}; | ||||
| 		} | ||||
| 		case z3::check_result::unsat: | ||||
| 		{ | ||||
| @ -104,8 +98,119 @@ pair<CheckResult, vector<string>> Z3CHCInterface::query(Expression const& _expr) | ||||
| 	catch (z3::exception const&) | ||||
| 	{ | ||||
| 		result = CheckResult::ERROR; | ||||
| 		values.clear(); | ||||
| 		cex = {}; | ||||
| 	} | ||||
| 
 | ||||
| 	return make_pair(result, values); | ||||
| 	return {result, cex}; | ||||
| } | ||||
| 
 | ||||
| void Z3CHCInterface::setSpacerOptions(bool _preProcessing) | ||||
| { | ||||
| 	// Spacer options.
 | ||||
| 	// These needs to be set in the solver.
 | ||||
| 	// https://github.com/Z3Prover/z3/blob/master/src/muz/base/fp_params.pyg
 | ||||
| 	z3::params p(*m_context); | ||||
| 	// These are useful for solving problems with arrays and loops.
 | ||||
| 	// Use quantified lemma generalizer.
 | ||||
| 	p.set("fp.spacer.q3.use_qgen", true); | ||||
| 	p.set("fp.spacer.mbqi", false); | ||||
| 	// Ground pobs by using values from a model.
 | ||||
| 	p.set("fp.spacer.ground_pobs", false); | ||||
| 
 | ||||
| 	// Spacer optimization should be
 | ||||
| 	// - enabled for better solving (default)
 | ||||
| 	// - disable for counterexample generation
 | ||||
| 	p.set("fp.xform.slice", _preProcessing); | ||||
| 	p.set("fp.xform.inline_linear", _preProcessing); | ||||
| 	p.set("fp.xform.inline_eager", _preProcessing); | ||||
| 
 | ||||
| 	m_solver.set(p); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| Convert a ground refutation into a linear or nonlinear counterexample. | ||||
| The counterexample is given as an implication graph of the form | ||||
| `premises => conclusion` where `premises` are the predicates | ||||
| from the body of nonlinear clauses, representing the proof graph. | ||||
| 
 | ||||
| This function is based on and similar to | ||||
| https://github.com/Z3Prover/z3/blob/z3-4.8.8/src/muz/spacer/spacer_context.cpp#L2919
 | ||||
| (spacer::context::get_ground_sat_answer) | ||||
| which generates linear counterexamples. | ||||
| It is modified here to accept nonlinear CHCs as well, generating a DAG | ||||
| instead of a path. | ||||
| */ | ||||
| CHCSolverInterface::CexGraph Z3CHCInterface::cexGraph(z3::expr const& _proof) | ||||
| { | ||||
| 	CexGraph graph; | ||||
| 
 | ||||
| 	/// The root fact of the refutation proof is `false`.
 | ||||
| 	/// The node itself is not a hyper resolution, so we need to
 | ||||
| 	/// extract the `query` hyper resolution node from the
 | ||||
| 	/// `false` node (the first child).
 | ||||
| 	smtAssert(_proof.is_app(), ""); | ||||
| 	smtAssert(fact(_proof).decl().decl_kind() == Z3_OP_FALSE, ""); | ||||
| 
 | ||||
| 	stack<z3::expr> proofStack; | ||||
| 	proofStack.push(_proof.arg(0)); | ||||
| 
 | ||||
| 	auto const& root = proofStack.top(); | ||||
| 	graph.nodes[root.id()] = {name(fact(root)), arguments(fact(root))}; | ||||
| 
 | ||||
| 	set<unsigned> visited; | ||||
| 	visited.insert(root.id()); | ||||
| 
 | ||||
| 	while (!proofStack.empty()) | ||||
| 	{ | ||||
| 		z3::expr proofNode = proofStack.top(); | ||||
| 		smtAssert(graph.nodes.count(proofNode.id()), ""); | ||||
| 		proofStack.pop(); | ||||
| 
 | ||||
| 		if (proofNode.is_app() && proofNode.decl().decl_kind() == Z3_OP_PR_HYPER_RESOLVE) | ||||
| 		{ | ||||
| 			smtAssert(proofNode.num_args() > 0, ""); | ||||
| 			for (unsigned i = 1; i < proofNode.num_args() - 1; ++i) | ||||
| 			{ | ||||
| 				z3::expr child = proofNode.arg(i); | ||||
| 				if (!visited.count(child.id())) | ||||
| 				{ | ||||
| 					visited.insert(child.id()); | ||||
| 					proofStack.push(child); | ||||
| 				} | ||||
| 
 | ||||
| 				if (!graph.nodes.count(child.id())) | ||||
| 				{ | ||||
| 					graph.nodes[child.id()] = {name(fact(child)), arguments(fact(child))}; | ||||
| 					graph.edges[child.id()] = {}; | ||||
| 				} | ||||
| 
 | ||||
| 				graph.edges[proofNode.id()].push_back(child.id()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return graph; | ||||
| } | ||||
| 
 | ||||
| z3::expr Z3CHCInterface::fact(z3::expr const& _node) | ||||
| { | ||||
| 	smtAssert(_node.is_app(), ""); | ||||
| 	if (_node.num_args() == 0) | ||||
| 		return _node; | ||||
| 	return _node.arg(_node.num_args() - 1); | ||||
| } | ||||
| 
 | ||||
| string Z3CHCInterface::name(z3::expr const& _predicate) | ||||
| { | ||||
| 	smtAssert(_predicate.is_app(), ""); | ||||
| 	return _predicate.decl().name().str(); | ||||
| } | ||||
| 
 | ||||
| vector<string> Z3CHCInterface::arguments(z3::expr const& _predicate) | ||||
| { | ||||
| 	smtAssert(_predicate.is_app(), ""); | ||||
| 	vector<string> args; | ||||
| 	for (unsigned i = 0; i < _predicate.num_args(); ++i) | ||||
| 		args.emplace_back(_predicate.arg(i).to_string()); | ||||
| 	return args; | ||||
| } | ||||
|  | ||||
| @ -25,6 +25,8 @@ | ||||
| #include <libsmtutil/CHCSolverInterface.h> | ||||
| #include <libsmtutil/Z3Interface.h> | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace solidity::smtutil | ||||
| { | ||||
| 
 | ||||
| @ -40,11 +42,22 @@ public: | ||||
| 
 | ||||
| 	void addRule(Expression const& _expr, std::string const& _name) override; | ||||
| 
 | ||||
| 	std::pair<CheckResult, std::vector<std::string>> query(Expression const& _expr) override; | ||||
| 	std::pair<CheckResult, CexGraph> query(Expression const& _expr) override; | ||||
| 
 | ||||
| 	Z3Interface* z3Interface() const { return m_z3Interface.get(); } | ||||
| 
 | ||||
| 	void setSpacerOptions(bool _preProcessing = true); | ||||
| 
 | ||||
| private: | ||||
| 	/// Constructs a nonlinear counterexample graph from the refutation.
 | ||||
| 	CHCSolverInterface::CexGraph cexGraph(z3::expr const& _proof); | ||||
| 	/// @returns the fact from a proof node.
 | ||||
| 	z3::expr fact(z3::expr const& _node); | ||||
| 	/// @returns @a _predicate's name.
 | ||||
| 	std::string name(z3::expr const& _predicate); | ||||
| 	/// @returns the arguments of @a _predicate.
 | ||||
| 	std::vector<std::string> arguments(z3::expr const& _predicate); | ||||
| 
 | ||||
| 	// Used to handle variables.
 | ||||
| 	std::unique_ptr<Z3Interface> m_z3Interface; | ||||
| 
 | ||||
|  | ||||
| @ -122,8 +122,9 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr | ||||
| 	/// Checks that two events with the same name defined in this contract have different
 | ||||
| 	/// argument types
 | ||||
| 	map<string, vector<EventDefinition const*>> events; | ||||
| 	for (EventDefinition const* event: _contract.events()) | ||||
| 		events[event->name()].push_back(event); | ||||
| 	for (auto const* contract: _contract.annotation().linearizedBaseContracts) | ||||
| 		for (EventDefinition const* event: contract->events()) | ||||
| 			events[event->name()].push_back(event); | ||||
| 
 | ||||
| 	findDuplicateDefinitions(events); | ||||
| } | ||||
|  | ||||
| @ -113,18 +113,16 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct) | ||||
| 		for (ASTPointer<VariableDeclaration> const& member: _struct.members()) | ||||
| 		{ | ||||
| 			Type const* memberType = member->annotation().type; | ||||
| 			while (auto arrayType = dynamic_cast<ArrayType const*>(memberType)) | ||||
| 			{ | ||||
| 				if (arrayType->isDynamicallySized()) | ||||
| 					break; | ||||
| 				memberType = arrayType->baseType(); | ||||
| 			} | ||||
| 
 | ||||
| 			if (auto arrayType = dynamic_cast<ArrayType const*>(memberType)) | ||||
| 				memberType = arrayType->finalBaseType(true); | ||||
| 
 | ||||
| 			if (auto structType = dynamic_cast<StructType const*>(memberType)) | ||||
| 				if (_cycleDetector.run(structType->structDefinition())) | ||||
| 					return; | ||||
| 		} | ||||
| 	}; | ||||
| 	if (util::CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr) | ||||
| 	if (util::CycleDetector<StructDefinition>(visitor).run(_struct)) | ||||
| 		m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition."); | ||||
| 
 | ||||
| 	return false; | ||||
| @ -296,15 +294,6 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) | ||||
| 			"The \"immutable\" keyword can only be used for state variables." | ||||
| 		); | ||||
| 
 | ||||
| 	if (!_variable.typeName()) | ||||
| 	{ | ||||
| 		// This can still happen in very unusual cases where a developer uses constructs, such as
 | ||||
| 		// `var a;`, however, such code will have generated errors already.
 | ||||
| 		// However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is
 | ||||
| 		// invoking ReferencesResolver) is generating it, so the error is most likely(!) generated
 | ||||
| 		// after this step.
 | ||||
| 		return; | ||||
| 	} | ||||
| 	using Location = VariableDeclaration::Location; | ||||
| 	Location varLoc = _variable.referenceLocation(); | ||||
| 	DataLocation typeLoc = DataLocation::Memory; | ||||
| @ -335,7 +324,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) | ||||
| 					", ", | ||||
| 					" or " | ||||
| 				); | ||||
| 			if (_variable.isCallableOrCatchParameter()) | ||||
| 			if (_variable.isConstructorParameter()) | ||||
| 				errorString += " for constructor parameter"; | ||||
| 			else if (_variable.isCallableOrCatchParameter()) | ||||
| 				errorString += | ||||
| 					" for " + | ||||
| 					string(_variable.isReturnParameter() ? "return " : "") + | ||||
| @ -385,7 +376,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) | ||||
| 				solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set."); | ||||
| 		} | ||||
| 
 | ||||
| 	TypePointer type = _variable.typeName()->annotation().type; | ||||
| 	TypePointer type = _variable.typeName().annotation().type; | ||||
| 	if (auto ref = dynamic_cast<ReferenceType const*>(type)) | ||||
| 	{ | ||||
| 		bool isPointer = !_variable.isStateVariable(); | ||||
|  | ||||
| @ -61,25 +61,12 @@ bool DocStringTagParser::visit(VariableDeclaration const& _variable) | ||||
| { | ||||
| 	if (_variable.isStateVariable()) | ||||
| 	{ | ||||
| 		static set<string> const validPublicTags = set<string>{"dev", "notice", "return", "title", "author", "inheritdoc"}; | ||||
| 		static set<string> const validPublicTags = set<string>{"dev", "notice", "return", "inheritdoc"}; | ||||
| 		static set<string> const validNonPublicTags = set<string>{"dev", "inheritdoc"}; | ||||
| 		if (_variable.isPublic()) | ||||
| 			parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables"); | ||||
| 		else | ||||
| 		{ | ||||
| 			parseDocStrings(_variable, _variable.annotation(), validPublicTags, "non-public state variables"); | ||||
| 			if (_variable.annotation().docTags.count("notice") > 0) | ||||
| 				m_errorReporter.warning( | ||||
| 					7816_error, _variable.documentation()->location(), | ||||
| 					"Documentation tag on non-public state variables will be disallowed in 0.7.0. " | ||||
| 					"You will need to use the @dev tag explicitly." | ||||
| 				); | ||||
| 		} | ||||
| 		if (_variable.annotation().docTags.count("title") > 0 || _variable.annotation().docTags.count("author") > 0) | ||||
| 			m_errorReporter.warning( | ||||
| 				8532_error, _variable.documentation()->location(), | ||||
| 				"Documentation tag @title and @author is only allowed on contract definitions. " | ||||
| 				"It will be disallowed in 0.7.0." | ||||
| 			); | ||||
| 			parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables"); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| @ -139,8 +126,8 @@ void DocStringTagParser::handleCallable( | ||||
| 	StructurallyDocumentedAnnotation& _annotation | ||||
| ) | ||||
| { | ||||
| 	static set<string> const validEventTags = set<string>{"author", "dev", "notice", "return", "param"}; | ||||
| 	static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param", "inheritdoc"}; | ||||
| 	static set<string> const validEventTags = set<string>{"dev", "notice", "return", "param"}; | ||||
| 	static set<string> const validTags = set<string>{"dev", "notice", "return", "param", "inheritdoc"}; | ||||
| 
 | ||||
| 	if (dynamic_cast<EventDefinition const*>(&_callable)) | ||||
| 		parseDocStrings(_node, _annotation, validEventTags, "events"); | ||||
| @ -148,13 +135,6 @@ void DocStringTagParser::handleCallable( | ||||
| 		parseDocStrings(_node, _annotation, validTags, "functions"); | ||||
| 
 | ||||
| 	checkParameters(_callable, _node, _annotation); | ||||
| 
 | ||||
| 	if (_node.documentation() && _annotation.docTags.count("author") > 0) | ||||
| 		m_errorReporter.warning( | ||||
| 			9843_error, _node.documentation()->location(), | ||||
| 			"Documentation tag @author is only allowed on contract definitions. " | ||||
| 			"It will be disallowed in 0.7.0." | ||||
| 		); | ||||
| } | ||||
| 
 | ||||
| void DocStringTagParser::parseDocStrings( | ||||
|  | ||||
| @ -290,7 +290,7 @@ StateMutability OverrideProxy::stateMutability() const | ||||
| 	return std::visit(GenericVisitor{ | ||||
| 		[&](FunctionDefinition const* _item) { return _item->stateMutability(); }, | ||||
| 		[&](ModifierDefinition const*) { solAssert(false, "Requested state mutability from modifier."); return StateMutability{}; }, | ||||
| 		[&](VariableDeclaration const*) { return StateMutability::View; } | ||||
| 		[&](VariableDeclaration const* _var) { return _var->isConstant() ? StateMutability::Pure : StateMutability::View; } | ||||
| 	}, m_item); | ||||
| } | ||||
| 
 | ||||
| @ -585,29 +585,35 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr | ||||
| 				"Overridden " + _overriding.astNodeName() + " is here:" | ||||
| 			); | ||||
| 
 | ||||
| 		// This is only relevant for a function overriding a function.
 | ||||
| 		if (_overriding.isFunction()) | ||||
| 		{ | ||||
| 			if (_overriding.stateMutability() != _super.stateMutability()) | ||||
| 				overrideError( | ||||
| 					_overriding, | ||||
| 					_super, | ||||
| 					6959_error, | ||||
| 					"Overriding function changes state mutability from \"" + | ||||
| 					stateMutabilityToString(_super.stateMutability()) + | ||||
| 					"\" to \"" + | ||||
| 					stateMutabilityToString(_overriding.stateMutability()) + | ||||
| 					"\"." | ||||
| 				); | ||||
| 		// Stricter mutability is always okay except when super is Payable
 | ||||
| 		if ( | ||||
| 			(_overriding.isFunction() || _overriding.isVariable()) && | ||||
| 			( | ||||
| 				_overriding.stateMutability() > _super.stateMutability() || | ||||
| 				_super.stateMutability() == StateMutability::Payable | ||||
| 			) && | ||||
| 			_overriding.stateMutability() != _super.stateMutability() | ||||
| 		) | ||||
| 			overrideError( | ||||
| 				_overriding, | ||||
| 				_super, | ||||
| 				6959_error, | ||||
| 				"Overriding " + | ||||
| 				_overriding.astNodeName() + | ||||
| 				" changes state mutability from \"" + | ||||
| 				stateMutabilityToString(_super.stateMutability()) + | ||||
| 				"\" to \"" + | ||||
| 				stateMutabilityToString(_overriding.stateMutability()) + | ||||
| 				"\"." | ||||
| 			); | ||||
| 
 | ||||
| 			if (_overriding.unimplemented() && !_super.unimplemented()) | ||||
| 				overrideError( | ||||
| 					_overriding, | ||||
| 					_super, | ||||
| 					4593_error, | ||||
| 					"Overriding an implemented function with an unimplemented function is not allowed." | ||||
| 				); | ||||
| 		} | ||||
| 		if (_overriding.unimplemented() && !_super.unimplemented()) | ||||
| 			overrideError( | ||||
| 				_overriding, | ||||
| 				_super, | ||||
| 				4593_error, | ||||
| 				"Overriding an implemented function with an unimplemented function is not allowed." | ||||
| 			); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -34,15 +34,16 @@ | ||||
| #include <liblangutil/Exceptions.h> | ||||
| 
 | ||||
| #include <libsolutil/StringUtils.h> | ||||
| #include <libsolutil/CommonData.h> | ||||
| 
 | ||||
| #include <boost/algorithm/string.hpp> | ||||
| #include <boost/algorithm/string/split.hpp> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::langutil; | ||||
| using namespace solidity::frontend; | ||||
| 
 | ||||
| namespace solidity::frontend | ||||
| { | ||||
| 
 | ||||
| bool ReferencesResolver::resolve(ASTNode const& _root) | ||||
| { | ||||
| @ -202,6 +203,10 @@ bool ReferencesResolver::visit(Return const& _return) | ||||
| 
 | ||||
| void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) | ||||
| { | ||||
| 	validateYulIdentifierName(_function.name, _function.location); | ||||
| 	for (yul::TypedName const& varName: _function.parameters + _function.returnVariables) | ||||
| 		validateYulIdentifierName(varName.name, varName.location); | ||||
| 
 | ||||
| 	bool wasInsideFunction = m_yulInsideFunction; | ||||
| 	m_yulInsideFunction = true; | ||||
| 	this->operator()(_function.body); | ||||
| @ -210,9 +215,10 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) | ||||
| 
 | ||||
| void ReferencesResolver::operator()(yul::Identifier const& _identifier) | ||||
| { | ||||
| 	bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); | ||||
| 	bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); | ||||
| 	bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), ".slot"); | ||||
| 	bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), ".offset"); | ||||
| 
 | ||||
| 	// Could also use `pathFromCurrentScope`, split by '.'
 | ||||
| 	auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); | ||||
| 	if (isSlot || isOffset) | ||||
| 	{ | ||||
| @ -222,19 +228,22 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) | ||||
| 			return; | ||||
| 		string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( | ||||
| 			isSlot ? | ||||
| 			string("_slot").size() : | ||||
| 			string("_offset").size() | ||||
| 			string(".slot").size() : | ||||
| 			string(".offset").size() | ||||
| 		)); | ||||
| 		if (realName.empty()) | ||||
| 		{ | ||||
| 			m_errorReporter.declarationError( | ||||
| 				4794_error, | ||||
| 				_identifier.location, | ||||
| 				"In variable names _slot and _offset can only be used as a suffix." | ||||
| 				"In variable names .slot and .offset can only be used as a suffix." | ||||
| 			); | ||||
| 			return; | ||||
| 		} | ||||
| 		declarations = m_resolver.nameFromCurrentScope(realName); | ||||
| 		if (!declarations.empty()) | ||||
| 			// To support proper path resolution, we have to use pathFromCurrentScope.
 | ||||
| 			solAssert(!util::contains(realName, '.'), ""); | ||||
| 	} | ||||
| 	if (declarations.size() > 1) | ||||
| 	{ | ||||
| @ -246,7 +255,18 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) | ||||
| 		return; | ||||
| 	} | ||||
| 	else if (declarations.size() == 0) | ||||
| 	{ | ||||
| 		if ( | ||||
| 			boost::algorithm::ends_with(_identifier.name.str(), "_slot") || | ||||
| 			boost::algorithm::ends_with(_identifier.name.str(), "_offset") | ||||
| 		) | ||||
| 			m_errorReporter.declarationError( | ||||
| 				9467_error, | ||||
| 				_identifier.location, | ||||
| 				"Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables." | ||||
| 			); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front())) | ||||
| 		if (var->isLocalVariable() && m_yulInsideFunction) | ||||
| 		{ | ||||
| @ -267,18 +287,11 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) | ||||
| { | ||||
| 	for (auto const& identifier: _varDecl.variables) | ||||
| 	{ | ||||
| 		bool isSlot = boost::algorithm::ends_with(identifier.name.str(), "_slot"); | ||||
| 		bool isOffset = boost::algorithm::ends_with(identifier.name.str(), "_offset"); | ||||
| 		validateYulIdentifierName(identifier.name, identifier.location); | ||||
| 
 | ||||
| 		string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.')); | ||||
| 		if (isSlot || isOffset) | ||||
| 			m_errorReporter.declarationError( | ||||
| 				9155_error, | ||||
| 				identifier.location, | ||||
| 				"In variable declarations _slot and _offset can not be used as a suffix." | ||||
| 			); | ||||
| 		else if ( | ||||
| 			auto declarations = m_resolver.nameFromCurrentScope(namePrefix); | ||||
| 
 | ||||
| 		if ( | ||||
| 			auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str()); | ||||
| 			!declarations.empty() | ||||
| 		) | ||||
| 		{ | ||||
| @ -290,8 +303,6 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) | ||||
| 					3859_error, | ||||
| 					identifier.location, | ||||
| 					ssl, | ||||
| 					namePrefix.size() < identifier.name.str().size() ? | ||||
| 					"The prefix of this declaration conflicts with a declaration outside the inline assembly block." : | ||||
| 					"This declaration shadows a declaration outside the inline assembly block." | ||||
| 				); | ||||
| 		} | ||||
| @ -350,4 +361,12 @@ void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _docum | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ReferencesResolver::validateYulIdentifierName(yul::YulString _name, SourceLocation const& _location) | ||||
| { | ||||
| 	if (util::contains(_name.str(), '.')) | ||||
| 		m_errorReporter.declarationError( | ||||
| 			3927_error, | ||||
| 			_location, | ||||
| 			"User-defined identifiers in inline assembly cannot contain '.'." | ||||
| 		); | ||||
| } | ||||
|  | ||||
| @ -92,6 +92,9 @@ private: | ||||
| 
 | ||||
| 	void resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation); | ||||
| 
 | ||||
| 	/// Checks if the name contains a '.'.
 | ||||
| 	void validateYulIdentifierName(yul::YulString _name, langutil::SourceLocation const& _location); | ||||
| 
 | ||||
| 	langutil::ErrorReporter& m_errorReporter; | ||||
| 	NameAndTypeResolver& m_resolver; | ||||
| 	langutil::EVMVersion m_evmVersion; | ||||
|  | ||||
| @ -28,6 +28,8 @@ | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| #include <liblangutil/SemVerHandler.h> | ||||
| 
 | ||||
| #include <libsolutil/UTF8.h> | ||||
| 
 | ||||
| #include <boost/algorithm/string.hpp> | ||||
| 
 | ||||
| #include <memory> | ||||
| @ -37,7 +39,7 @@ using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::langutil; | ||||
| using namespace solidity::frontend; | ||||
| 
 | ||||
| using namespace solidity::util; | ||||
| 
 | ||||
| bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) | ||||
| { | ||||
| @ -217,6 +219,13 @@ bool SyntaxChecker::visit(Throw const& _throwStatement) | ||||
| 
 | ||||
| bool SyntaxChecker::visit(Literal const& _literal) | ||||
| { | ||||
| 	if ((_literal.token() == Token::UnicodeStringLiteral) && !validateUTF8(_literal.value())) | ||||
| 		m_errorReporter.syntaxError( | ||||
| 			8452_error, | ||||
| 			_literal.location(), | ||||
| 			"Invalid UTF-8 sequence found" | ||||
| 		); | ||||
| 
 | ||||
| 	if (_literal.token() != Token::Number) | ||||
| 		return true; | ||||
| 
 | ||||
| @ -302,7 +311,7 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) | ||||
| 
 | ||||
| bool SyntaxChecker::visit(FunctionDefinition const& _function) | ||||
| { | ||||
| 	if (_function.noVisibilitySpecified()) | ||||
| 	if (!_function.isConstructor() && _function.noVisibilitySpecified()) | ||||
| 	{ | ||||
| 		string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; | ||||
| 		m_errorReporter.syntaxError( | ||||
|  | ||||
| @ -327,10 +327,14 @@ bool TypeChecker::visit(FunctionDefinition const& _function) | ||||
| { | ||||
| 	if (_function.markedVirtual()) | ||||
| 	{ | ||||
| 		if (_function.annotation().contract->isInterface()) | ||||
| 		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\""); | ||||
| 		if (_function.visibility() == Visibility::Private) | ||||
| 		else if (_function.visibility() == Visibility::Private) | ||||
| 			m_errorReporter.typeError(3942_error, _function.location(), "\"virtual\" and \"private\" cannot be used together."); | ||||
| 		else if (_function.libraryFunction()) | ||||
| 			m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); | ||||
| 	} | ||||
| 
 | ||||
| 	if (_function.isPayable()) | ||||
| @ -340,45 +344,62 @@ bool TypeChecker::visit(FunctionDefinition const& _function) | ||||
| 		if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) | ||||
| 			m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); | ||||
| 	} | ||||
| 	auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) { | ||||
| 		if (type(var)->category() == Type::Category::Mapping) | ||||
| 		{ | ||||
| 			if (var.referenceLocation() != VariableDeclaration::Location::Storage) | ||||
| 			{ | ||||
| 				if (!_function.libraryFunction() && _function.isPublic()) | ||||
| 					m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions."); | ||||
| 				else | ||||
| 					m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." ); | ||||
| 			} | ||||
| 			else | ||||
| 				solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions."); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if (!type(var)->canLiveOutsideStorage() && _function.isPublic()) | ||||
| 				m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage."); | ||||
| 			if (_function.isPublic()) | ||||
| 			{ | ||||
| 				auto iType = type(var)->interfaceType(_function.libraryFunction()); | ||||
| 
 | ||||
| 				if (!iType) | ||||
| 				{ | ||||
| 					solAssert(!iType.message().empty(), "Expected detailed error message!"); | ||||
| 					m_errorReporter.fatalTypeError(4103_error, var.location(), iType.message()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	vector<VariableDeclaration const*> internalParametersInConstructor; | ||||
| 
 | ||||
| 	auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& _var) { | ||||
| 		if (type(_var)->containsNestedMapping()) | ||||
| 			if (_var.referenceLocation() == VariableDeclaration::Location::Storage) | ||||
| 				solAssert( | ||||
| 					_function.libraryFunction() || _function.isConstructor() || !_function.isPublic(), | ||||
| 					"Mapping types for parameters or return variables " | ||||
| 					"can only be used in internal or library functions." | ||||
| 				); | ||||
| 		bool functionIsExternallyVisible = | ||||
| 			(!_function.isConstructor() && _function.isPublic()) || | ||||
| 			(_function.isConstructor() && !m_currentContract->abstract()); | ||||
| 		if ( | ||||
| 			_function.isPublic() && | ||||
| 			!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && | ||||
| 			!typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction()) | ||||
| 			_function.isConstructor() && | ||||
| 			_var.referenceLocation() == VariableDeclaration::Location::Storage && | ||||
| 			!m_currentContract->abstract() | ||||
| 		) | ||||
| 			m_errorReporter.typeError( | ||||
| 				4957_error, | ||||
| 				var.location(), | ||||
| 				"This type is only supported in ABIEncoderV2. " | ||||
| 				"Use \"pragma experimental ABIEncoderV2;\" to enable the feature." | ||||
| 				3644_error, | ||||
| 				_var.location(), | ||||
| 				"This parameter has a type that can only be used internally. " | ||||
| 				"You can make the contract abstract to avoid this problem." | ||||
| 			); | ||||
| 		else if (functionIsExternallyVisible) | ||||
| 		{ | ||||
| 			auto iType = type(_var)->interfaceType(_function.libraryFunction()); | ||||
| 
 | ||||
| 			if (!iType) | ||||
| 			{ | ||||
| 				string message = iType.message(); | ||||
| 				solAssert(!message.empty(), "Expected detailed error message!"); | ||||
| 				if (_function.isConstructor()) | ||||
| 					message += " You can make the contract abstract to avoid this problem."; | ||||
| 				m_errorReporter.typeError(4103_error, _var.location(), message); | ||||
| 			} | ||||
| 			else if ( | ||||
| 				!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && | ||||
| 				!typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction()) | ||||
| 			) | ||||
| 			{ | ||||
| 				string message = | ||||
| 					"This type is only supported in ABIEncoderV2. " | ||||
| 					"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."; | ||||
| 				if (_function.isConstructor()) | ||||
| 					message += | ||||
| 						" Alternatively, make the contract abstract and supply the " | ||||
| 						"constructor arguments from a derived contract."; | ||||
| 				m_errorReporter.typeError( | ||||
| 					4957_error, | ||||
| 					_var.location(), | ||||
| 					message | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
| 	for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) | ||||
| 	{ | ||||
| @ -390,6 +411,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) | ||||
| 		checkArgumentAndReturnParameter(*var); | ||||
| 		var->accept(*this); | ||||
| 	} | ||||
| 
 | ||||
| 	set<Declaration const*> modifiers; | ||||
| 	for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers()) | ||||
| 	{ | ||||
| @ -415,11 +437,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function) | ||||
| 		if (_function.isImplemented()) | ||||
| 			m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); | ||||
| 
 | ||||
| 		if (_function.visibility() != Visibility::External) | ||||
| 			m_errorReporter.typeError(1560_error, _function.location(), "Functions in interfaces must be declared external."); | ||||
| 
 | ||||
| 		if (_function.isConstructor()) | ||||
| 			m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces."); | ||||
| 		else if (_function.visibility() != Visibility::External) | ||||
| 			m_errorReporter.typeError(1560_error, _function.location(), "Functions in interfaces must be declared external."); | ||||
| 	} | ||||
| 	else if (m_currentContract->contractKind() == ContractKind::Library) | ||||
| 		if (_function.isConstructor()) | ||||
| @ -446,8 +467,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) | ||||
| 
 | ||||
| bool TypeChecker::visit(VariableDeclaration const& _variable) | ||||
| { | ||||
| 	if (_variable.typeName()) | ||||
| 		_variable.typeName()->accept(*this); | ||||
| 	_variable.typeName().accept(*this); | ||||
| 
 | ||||
| 	// type is filled either by ReferencesResolver directly from the type name or by
 | ||||
| 	// TypeChecker at the VariableDeclarationStatement level.
 | ||||
| @ -459,9 +479,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) | ||||
| 			m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library."); | ||||
| 	if (_variable.value()) | ||||
| 	{ | ||||
| 		if (_variable.isStateVariable() && dynamic_cast<MappingType const*>(varType)) | ||||
| 		if (_variable.isStateVariable() && varType->containsNestedMapping()) | ||||
| 		{ | ||||
| 			m_errorReporter.typeError(6280_error, _variable.location(), "Mappings cannot be assigned to."); | ||||
| 			m_errorReporter.typeError( | ||||
| 				6280_error, | ||||
| 				_variable.location(), | ||||
| 				"Types in storage containing (nested) mappings cannot be assigned to." | ||||
| 			); | ||||
| 			_variable.value()->accept(*this); | ||||
| 		} | ||||
| 		else | ||||
| @ -501,9 +525,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) | ||||
| 
 | ||||
| 	if (!_variable.isStateVariable()) | ||||
| 	{ | ||||
| 		if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) | ||||
| 			if (!varType->canLiveOutsideStorage()) | ||||
| 				m_errorReporter.typeError(4061_error, _variable.location(), "Type " + varType->toString() + " is only valid in storage."); | ||||
| 		if ( | ||||
| 			_variable.referenceLocation() == VariableDeclaration::Location::CallData || | ||||
| 			_variable.referenceLocation() == VariableDeclaration::Location::Memory | ||||
| 		) | ||||
| 			if (varType->containsNestedMapping()) | ||||
| 				m_errorReporter.fatalTypeError( | ||||
| 					4061_error, | ||||
| 					_variable.location(), | ||||
| 					"Type " + varType->toString(true) + " is only valid in storage because it contains a (nested) mapping." | ||||
| 				); | ||||
| 	} | ||||
| 	else if (_variable.visibility() >= Visibility::Public) | ||||
| 	{ | ||||
| @ -534,7 +565,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) | ||||
| 	if (auto referenceType = dynamic_cast<ReferenceType const*>(varType)) | ||||
| 	{ | ||||
| 		auto result = referenceType->validForLocation(referenceType->location()); | ||||
| 		if (result && _variable.isPublicCallableParameter()) | ||||
| 		if (result && (_variable.isConstructorParameter() || _variable.isPublicCallableParameter())) | ||||
| 			result = referenceType->validForLocation(DataLocation::CallData); | ||||
| 		if (!result) | ||||
| 		{ | ||||
| @ -560,15 +591,11 @@ 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() ? _variable.typeName()->location() : _variable.location(), | ||||
| 					collisionMessage(varType->canonicalName(), false) | ||||
| 				); | ||||
| 				m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->canonicalName(), false)); | ||||
| 		} | ||||
| 		vector<Type const*> oversizedSubtypes = frontend::oversizedSubtypes(*varType); | ||||
| 		for (Type const* subtype: oversizedSubtypes) | ||||
| 			m_errorReporter.warning(7325_error, _variable.typeName()->location(), collisionMessage(subtype->canonicalName(), false)); | ||||
| 			m_errorReporter.warning(7325_error, _variable.typeName().location(), collisionMessage(subtype->canonicalName(), false)); | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| @ -646,8 +673,12 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) | ||||
| 	{ | ||||
| 		if (var->isIndexed()) | ||||
| 			numIndexed++; | ||||
| 		if (!type(*var)->canLiveOutsideStorage()) | ||||
| 			m_errorReporter.typeError(3448_error, var->location(), "Type is required to live outside storage."); | ||||
| 		if (type(*var)->containsNestedMapping()) | ||||
| 			m_errorReporter.typeError( | ||||
| 				3448_error, | ||||
| 				var->location(), | ||||
| 				"Type containing a (nested) mapping is not allowed as event parameter type." | ||||
| 			); | ||||
| 		if (!type(*var)->interfaceType(false)) | ||||
| 			m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); | ||||
| 		if ( | ||||
| @ -724,7 +755,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 				} | ||||
| 				else if (requiresStorage) | ||||
| 				{ | ||||
| 					m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables."); | ||||
| 					m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); | ||||
| 					return false; | ||||
| 				} | ||||
| 				else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast<Literal const*>(var->value().get())) | ||||
| @ -752,7 +783,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 			{ | ||||
| 				if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) | ||||
| 				{ | ||||
| 					m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); | ||||
| 					m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables."); | ||||
| 					return false; | ||||
| 				} | ||||
| 				else if (_context == yul::IdentifierContext::LValue) | ||||
| @ -764,7 +795,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 					} | ||||
| 					else if (identifierInfo.isOffset) | ||||
| 					{ | ||||
| 						m_errorReporter.typeError(9739_error, _identifier.location, "Only _slot can be assigned to."); | ||||
| 						m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); | ||||
| 						return false; | ||||
| 					} | ||||
| 					else | ||||
| @ -773,12 +804,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 			} | ||||
| 			else if (!var->isConstant() && var->isStateVariable()) | ||||
| 			{ | ||||
| 				m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); | ||||
| 				m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the .slot and .offset suffixes."); | ||||
| 				return false; | ||||
| 			} | ||||
| 			else if (var->type()->dataStoredIn(DataLocation::Storage)) | ||||
| 			{ | ||||
| 				m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables."); | ||||
| 				m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the .slot or .offset suffix to access storage reference variables."); | ||||
| 				return false; | ||||
| 			} | ||||
| 			else if (var->type()->sizeOnStack() != 1) | ||||
| @ -792,7 +823,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) | ||||
| 		} | ||||
| 		else if (requiresStorage) | ||||
| 		{ | ||||
| 			m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); | ||||
| 			m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables."); | ||||
| 			return false; | ||||
| 		} | ||||
| 		else if (_context == yul::IdentifierContext::LValue) | ||||
| @ -1071,81 +1102,23 @@ void TypeChecker::endVisit(EmitStatement const& _emit) | ||||
| 		m_errorReporter.typeError(9292_error, _emit.eventCall().expression().location(), "Expression has to be an event invocation."); | ||||
| } | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| /**
 | ||||
|  * @returns a suggested left-hand-side of a multi-variable declaration contairing | ||||
|  * the variable declarations given in @a _decls. | ||||
|  */ | ||||
| string createTupleDecl(vector<ASTPointer<VariableDeclaration>> const& _decls) | ||||
| { | ||||
| 	vector<string> components; | ||||
| 	for (ASTPointer<VariableDeclaration> const& decl: _decls) | ||||
| 		if (decl) | ||||
| 		{ | ||||
| 			solAssert(decl->annotation().type, ""); | ||||
| 			components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name()); | ||||
| 		} | ||||
| 		else | ||||
| 			components.emplace_back(); | ||||
| 
 | ||||
| 	if (_decls.size() == 1) | ||||
| 		return components.front(); | ||||
| 	else | ||||
| 		return "(" + boost::algorithm::join(components, ", ") + ")"; | ||||
| } | ||||
| 
 | ||||
| bool typeCanBeExpressed(vector<ASTPointer<VariableDeclaration>> const& decls) | ||||
| { | ||||
| 	for (ASTPointer<VariableDeclaration> const& decl: decls) | ||||
| 	{ | ||||
| 		// skip empty tuples (they can be expressed of course)
 | ||||
| 		if (!decl) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!decl->annotation().type) | ||||
| 			return false; | ||||
| 
 | ||||
| 		if (auto functionType = dynamic_cast<FunctionType const*>(decl->annotation().type)) | ||||
| 			if ( | ||||
| 				functionType->kind() != FunctionType::Kind::Internal && | ||||
| 				functionType->kind() != FunctionType::Kind::External | ||||
| 			) | ||||
| 				return false; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| bool TypeChecker::visit(VariableDeclarationStatement const& _statement) | ||||
| { | ||||
| 	if (!_statement.initialValue()) | ||||
| 	{ | ||||
| 		// No initial value is only permitted for single variables with specified type.
 | ||||
| 		// This usually already results in a parser error.
 | ||||
| 		if (_statement.declarations().size() != 1 || !_statement.declarations().front()) | ||||
| 		{ | ||||
| 			if (std::all_of( | ||||
| 				_statement.declarations().begin(), | ||||
| 				_statement.declarations().end(), | ||||
| 				[](ASTPointer<VariableDeclaration> const& declaration) { return declaration == nullptr; } | ||||
| 			)) | ||||
| 			{ | ||||
| 				// The syntax checker has already generated an error for this case (empty LHS tuple).
 | ||||
| 				solAssert(m_errorReporter.hasErrors(), ""); | ||||
| 			solAssert(m_errorReporter.hasErrors(), ""); | ||||
| 
 | ||||
| 				// It is okay to return here, as there are no named components on the
 | ||||
| 				// left-hand-side that could cause any damage later.
 | ||||
| 				return false; | ||||
| 			} | ||||
| 			else | ||||
| 				// Bailing out *fatal* here, as those (untyped) vars may be used later, and diagnostics wouldn't be helpful then.
 | ||||
| 				m_errorReporter.fatalTypeError(4626_error, _statement.location(), "Use of the \"var\" keyword is disallowed."); | ||||
| 			// It is okay to return here, as there are no named components on the
 | ||||
| 			// left-hand-side that could cause any damage later.
 | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		VariableDeclaration const& varDecl = *_statement.declarations().front(); | ||||
| 		if (!varDecl.annotation().type) | ||||
| 			m_errorReporter.fatalTypeError(6983_error, _statement.location(), "Use of the \"var\" keyword is disallowed."); | ||||
| 		solAssert(varDecl.annotation().type, ""); | ||||
| 
 | ||||
| 		if (dynamic_cast<MappingType const*>(type(varDecl))) | ||||
| 			m_errorReporter.typeError( | ||||
| @ -1182,8 +1155,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) | ||||
| 			")." | ||||
| 		); | ||||
| 
 | ||||
| 	bool autoTypeDeductionNeeded = false; | ||||
| 
 | ||||
| 	for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i) | ||||
| 	{ | ||||
| 		if (!variables[i]) | ||||
| @ -1192,95 +1163,45 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) | ||||
| 		solAssert(!var.value(), "Value has to be tied to statement."); | ||||
| 		TypePointer const& valueComponentType = valueTypes[i]; | ||||
| 		solAssert(!!valueComponentType, ""); | ||||
| 		if (!var.annotation().type) | ||||
| 		{ | ||||
| 			autoTypeDeductionNeeded = true; | ||||
| 		solAssert(var.annotation().type, ""); | ||||
| 
 | ||||
| 			// Infer type from value.
 | ||||
| 			solAssert(!var.typeName(), ""); | ||||
| 			var.annotation().type = valueComponentType->mobileType(); | ||||
| 			if (!var.annotation().type) | ||||
| 			{ | ||||
| 				if (valueComponentType->category() == Type::Category::RationalNumber) | ||||
| 					m_errorReporter.fatalTypeError( | ||||
| 						6963_error, | ||||
| 						_statement.initialValue()->location(), | ||||
| 						"Invalid rational " + | ||||
| 						valueComponentType->toString() + | ||||
| 						" (absolute value too large or division by zero)." | ||||
| 					); | ||||
| 				else | ||||
| 					solAssert(false, ""); | ||||
| 			} | ||||
| 			else if (*var.annotation().type == *TypeProvider::emptyTuple()) | ||||
| 				solAssert(false, "Cannot declare variable with void (empty tuple) type."); | ||||
| 			else if (valueComponentType->category() == Type::Category::RationalNumber) | ||||
| 			{ | ||||
| 				string typeName = var.annotation().type->toString(true); | ||||
| 				string extension; | ||||
| 				if (auto type = dynamic_cast<IntegerType const*>(var.annotation().type)) | ||||
| 				{ | ||||
| 					unsigned numBits = type->numBits(); | ||||
| 					bool isSigned = type->isSigned(); | ||||
| 					solAssert(numBits > 0, ""); | ||||
| 					string minValue; | ||||
| 					string maxValue; | ||||
| 					if (isSigned) | ||||
| 					{ | ||||
| 						numBits--; | ||||
| 						minValue = "-" + bigint(bigint(1) << numBits).str(); | ||||
| 					} | ||||
| 					else | ||||
| 						minValue = "0"; | ||||
| 					maxValue = bigint((bigint(1) << numBits) - 1).str(); | ||||
| 					extension = ", which can hold values between " + minValue + " and " + maxValue; | ||||
| 				} | ||||
| 				else | ||||
| 					solAssert(dynamic_cast<FixedPointType const*>(var.annotation().type), "Unknown type."); | ||||
| 			} | ||||
| 
 | ||||
| 			var.accept(*this); | ||||
| 		} | ||||
| 		else | ||||
| 		var.accept(*this); | ||||
| 		BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type); | ||||
| 		if (!result) | ||||
| 		{ | ||||
| 			var.accept(*this); | ||||
| 			BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type); | ||||
| 			if (!result) | ||||
| 			auto errorMsg = "Type " + | ||||
| 				valueComponentType->toString() + | ||||
| 				" is not implicitly convertible to expected type " + | ||||
| 				var.annotation().type->toString(); | ||||
| 			if ( | ||||
| 				valueComponentType->category() == Type::Category::RationalNumber && | ||||
| 				dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() && | ||||
| 				valueComponentType->mobileType() | ||||
| 			) | ||||
| 			{ | ||||
| 				auto errorMsg = "Type " + | ||||
| 					valueComponentType->toString() + | ||||
| 					" is not implicitly convertible to expected type " + | ||||
| 					var.annotation().type->toString(); | ||||
| 				if ( | ||||
| 					valueComponentType->category() == Type::Category::RationalNumber && | ||||
| 					dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() && | ||||
| 					valueComponentType->mobileType() | ||||
| 				) | ||||
| 				{ | ||||
| 					if (var.annotation().type->operator==(*valueComponentType->mobileType())) | ||||
| 						m_errorReporter.typeError( | ||||
| 							5107_error, | ||||
| 							_statement.location(), | ||||
| 							errorMsg + ", but it can be explicitly converted." | ||||
| 						); | ||||
| 					else | ||||
| 						m_errorReporter.typeError( | ||||
| 							4486_error, | ||||
| 							_statement.location(), | ||||
| 							errorMsg + | ||||
| 							". Try converting to type " + | ||||
| 							valueComponentType->mobileType()->toString() + | ||||
| 							" or use an explicit conversion." | ||||
| 						); | ||||
| 				} | ||||
| 				else | ||||
| 					m_errorReporter.typeErrorConcatenateDescriptions( | ||||
| 						9574_error, | ||||
| 				if (var.annotation().type->operator==(*valueComponentType->mobileType())) | ||||
| 					m_errorReporter.typeError( | ||||
| 						5107_error, | ||||
| 						_statement.location(), | ||||
| 						errorMsg + ".", | ||||
| 						result.message() | ||||
| 						errorMsg + ", but it can be explicitly converted." | ||||
| 					); | ||||
| 				else | ||||
| 					m_errorReporter.typeError( | ||||
| 						4486_error, | ||||
| 						_statement.location(), | ||||
| 						errorMsg + | ||||
| 						". Try converting to type " + | ||||
| 						valueComponentType->mobileType()->toString() + | ||||
| 						" or use an explicit conversion." | ||||
| 					); | ||||
| 			} | ||||
| 			else | ||||
| 				m_errorReporter.typeErrorConcatenateDescriptions( | ||||
| 					9574_error, | ||||
| 					_statement.location(), | ||||
| 					errorMsg + ".", | ||||
| 					result.message() | ||||
| 				); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -1292,24 +1213,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) | ||||
| 				BOOST_THROW_EXCEPTION(FatalError()); | ||||
| 	} | ||||
| 
 | ||||
| 	if (autoTypeDeductionNeeded) | ||||
| 	{ | ||||
| 		if (!typeCanBeExpressed(variables)) | ||||
| 			m_errorReporter.syntaxError( | ||||
| 				3478_error, | ||||
| 				_statement.location(), | ||||
| 				"Use of the \"var\" keyword is disallowed. " | ||||
| 				"Type cannot be expressed in syntax." | ||||
| 			); | ||||
| 		else | ||||
| 			m_errorReporter.syntaxError( | ||||
| 				1719_error, | ||||
| 				_statement.location(), | ||||
| 				"Use of the \"var\" keyword is disallowed. " | ||||
| 				"Use explicit declaration `" + createTupleDecl(variables) + " = ...` instead." | ||||
| 			); | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| @ -1390,9 +1293,9 @@ bool TypeChecker::visit(Conditional const& _conditional) | ||||
| 
 | ||||
| 	if (_conditional.annotation().willBeWrittenTo) | ||||
| 		m_errorReporter.typeError( | ||||
| 				2212_error, | ||||
| 				_conditional.location(), | ||||
| 				"Conditional expression as left value is not supported yet." | ||||
| 			2212_error, | ||||
| 			_conditional.location(), | ||||
| 			"Conditional expression as left value is not supported yet." | ||||
| 		); | ||||
| 
 | ||||
| 	return false; | ||||
| @ -1420,7 +1323,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& | ||||
| 				checkExpressionAssignment(*types[i], *tupleExpression->components()[i]); | ||||
| 			} | ||||
| 	} | ||||
| 	else if (_type.category() == Type::Category::Mapping) | ||||
| 	else if (_type.nameable() && _type.containsNestedMapping()) | ||||
| 	{ | ||||
| 		bool isLocalOrReturn = false; | ||||
| 		if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression)) | ||||
| @ -1428,7 +1331,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& | ||||
| 				if (variableDeclaration->isLocalOrReturn()) | ||||
| 					isLocalOrReturn = true; | ||||
| 		if (!isLocalOrReturn) | ||||
| 			m_errorReporter.typeError(9214_error, _expression.location(), "Mappings cannot be assigned to."); | ||||
| 			m_errorReporter.typeError(9214_error, _expression.location(), "Types in storage containing (nested) mappings cannot be assigned to."); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -1564,8 +1467,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple) | ||||
| 					_tuple.location(), | ||||
| 					"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element." | ||||
| 				); | ||||
| 			else if (!inlineArrayType->canLiveOutsideStorage()) | ||||
| 				m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); | ||||
| 			else if (inlineArrayType->containsNestedMapping()) | ||||
| 				m_errorReporter.fatalTypeError( | ||||
| 					1545_error, | ||||
| 					_tuple.location(), | ||||
| 					"Type " + inlineArrayType->toString(true) + " is only valid in storage." | ||||
| 				); | ||||
| 
 | ||||
| 			_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size()); | ||||
| 		} | ||||
| @ -1906,8 +1813,6 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) | ||||
| void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function) | ||||
| { | ||||
| 	solAssert(_function.isConstructor(), ""); | ||||
| 	if (_function.markedVirtual()) | ||||
| 		m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); | ||||
| 	if (_function.overrides()) | ||||
| 		m_errorReporter.typeError(1209_error, _function.location(), "Constructors cannot override."); | ||||
| 	if (!_function.returnParameters().empty()) | ||||
| @ -1920,8 +1825,30 @@ void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function) | ||||
| 			stateMutabilityToString(_function.stateMutability()) + | ||||
| 			"\"." | ||||
| 		); | ||||
| 	if (_function.visibility() != Visibility::Public && _function.visibility() != Visibility::Internal) | ||||
| 		m_errorReporter.typeError(9239_error, _function.location(), "Constructor must be public or internal."); | ||||
| 	if (!_function.noVisibilitySpecified()) | ||||
| 	{ | ||||
| 		auto const& contract = dynamic_cast<ContractDefinition const&>(*_function.scope()); | ||||
| 		if (_function.visibility() != Visibility::Public && _function.visibility() != Visibility::Internal) | ||||
| 			m_errorReporter.typeError(9239_error, _function.location(), "Constructor cannot have visibility."); | ||||
| 		else if (_function.isPublic() && contract.abstract()) | ||||
| 			m_errorReporter.declarationError( | ||||
| 				8295_error, | ||||
| 				_function.location(), | ||||
| 				"Abstract contracts cannot have public constructors. Remove the \"public\" keyword to fix this." | ||||
| 			); | ||||
| 		else if (!_function.isPublic() && !contract.abstract()) | ||||
| 			m_errorReporter.declarationError( | ||||
| 				1845_error, | ||||
| 				_function.location(), | ||||
| 				"Non-abstract contracts cannot have internal constructors. Remove the \"internal\" keyword and make the contract abstract to fix this." | ||||
| 			); | ||||
| 		else | ||||
| 			m_errorReporter.warning( | ||||
| 				2462_error, | ||||
| 				_function.location(), | ||||
| 				"Visibility for constructor is ignored. If you want the contract to be non-deployable, making it \"abstract\" is sufficient." | ||||
| 			); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void TypeChecker::typeCheckABIEncodeFunctions( | ||||
| @ -2066,24 +1993,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks( | ||||
| 				toString(parameterTypes.size()) + | ||||
| 				"."; | ||||
| 
 | ||||
| 			// Extend error message in case we try to construct a struct with mapping member.
 | ||||
| 			if (isStructConstructorCall) | ||||
| 			{ | ||||
| 				/// For error message: Struct members that were removed during conversion to memory.
 | ||||
| 				TypePointer const expressionType = type(_functionCall.expression()); | ||||
| 				auto const& t = dynamic_cast<TypeType const&>(*expressionType); | ||||
| 				auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); | ||||
| 				set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory(); | ||||
| 
 | ||||
| 				if (!membersRemovedForStructConstructor.empty()) | ||||
| 				{ | ||||
| 					msg += " Members that have to be skipped in memory:"; | ||||
| 					for (auto const& member: membersRemovedForStructConstructor) | ||||
| 						msg += " " + member; | ||||
| 				} | ||||
| 
 | ||||
| 				return { isVariadic ? 1123_error : 9755_error, msg }; | ||||
| 			} | ||||
| 			else if ( | ||||
| 				_functionType->kind() == FunctionType::Kind::BareCall || | ||||
| 				_functionType->kind() == FunctionType::Kind::BareCallCode || | ||||
| @ -2303,6 +2214,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) | ||||
| 
 | ||||
| 		if (actualType->category() == Type::Category::Struct) | ||||
| 		{ | ||||
| 			if (actualType->containsNestedMapping()) | ||||
| 				m_errorReporter.fatalTypeError( | ||||
| 					9515_error, | ||||
| 					_functionCall.location(), | ||||
| 					"Struct containing a (nested) mapping cannot be constructed." | ||||
| 				); | ||||
| 			functionType = dynamic_cast<StructType const&>(*actualType).constructorType(); | ||||
| 			funcCallAnno.kind = FunctionCallKind::StructConstructorCall; | ||||
| 			funcCallAnno.isPure = argumentsArePure; | ||||
| @ -2532,8 +2449,6 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) | ||||
| 			m_errorReporter.fatalTypeError(5540_error, _newExpression.location(), "Identifier is not a contract."); | ||||
| 		if (contract->isInterface()) | ||||
| 			m_errorReporter.fatalTypeError(2971_error, _newExpression.location(), "Cannot instantiate an interface."); | ||||
| 		if (!contract->constructorIsPublic()) | ||||
| 			m_errorReporter.typeError(9054_error, _newExpression.location(), "Contract with internal constructor cannot be created directly."); | ||||
| 		if (contract->abstract()) | ||||
| 			m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); | ||||
| 
 | ||||
| @ -2554,11 +2469,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) | ||||
| 	} | ||||
| 	else if (type->category() == Type::Category::Array) | ||||
| 	{ | ||||
| 		if (!type->canLiveOutsideStorage()) | ||||
| 		if (type->containsNestedMapping()) | ||||
| 			m_errorReporter.fatalTypeError( | ||||
| 				1164_error, | ||||
| 				_newExpression.typeName().location(), | ||||
| 				"Type cannot live outside storage." | ||||
| 				"Array containing a (nested) mapping cannot be constructed in memory." | ||||
| 			); | ||||
| 		if (!type->isDynamicallySized()) | ||||
| 			m_errorReporter.typeError( | ||||
| @ -2716,7 +2631,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) | ||||
| 			!annotation.referencedDeclaration && | ||||
| 			(memberName == "value" || memberName == "gas") | ||||
| 		) | ||||
| 			m_errorReporter.warning( | ||||
| 			m_errorReporter.typeError( | ||||
| 				1621_error, | ||||
| 				_memberAccess.location(), | ||||
| 				"Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead." | ||||
| @ -3114,6 +3029,20 @@ bool TypeChecker::visit(Identifier const& _identifier) | ||||
| 			); | ||||
| 	} | ||||
| 
 | ||||
| 	if ( | ||||
| 		MagicVariableDeclaration const* magicVar = | ||||
| 		dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration) | ||||
| 	) | ||||
| 		if (magicVar->type()->category() == Type::Category::Integer) | ||||
| 		{ | ||||
| 			solAssert(_identifier.name() == "now", ""); | ||||
| 			m_errorReporter.typeError( | ||||
| 				7359_error, | ||||
| 				_identifier.location(), | ||||
| 				"\"now\" has been deprecated. Use \"block.timestamp\" instead." | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -169,7 +169,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) | ||||
| 		!_funDef.isConstructor() && | ||||
| 		!_funDef.isFallback() && | ||||
| 		!_funDef.isReceive() && | ||||
| 		!_funDef.overrides() | ||||
| 		!_funDef.virtualSemantics() | ||||
| 	) | ||||
| 		m_errorReporter.warning( | ||||
| 			2018_error, | ||||
|  | ||||
| @ -123,15 +123,9 @@ FunctionDefinition const* ContractDefinition::constructor() const | ||||
| 	return nullptr; | ||||
| } | ||||
| 
 | ||||
| bool ContractDefinition::constructorIsPublic() const | ||||
| { | ||||
| 	FunctionDefinition const* f = constructor(); | ||||
| 	return !f || f->isPublic(); | ||||
| } | ||||
| 
 | ||||
| bool ContractDefinition::canBeDeployed() const | ||||
| { | ||||
| 	return constructorIsPublic() && !abstract() && !isInterface(); | ||||
| 	return !abstract() && !isInterface(); | ||||
| } | ||||
| 
 | ||||
| FunctionDefinition const* ContractDefinition::fallbackFunction() const | ||||
| @ -295,6 +289,12 @@ bool FunctionDefinition::libraryFunction() const | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| Visibility FunctionDefinition::defaultVisibility() const | ||||
| { | ||||
| 	solAssert(!isConstructor(), ""); | ||||
| 	return Declaration::defaultVisibility(); | ||||
| } | ||||
| 
 | ||||
| FunctionTypePointer FunctionDefinition::functionType(bool _internal) const | ||||
| { | ||||
| 	if (_internal) | ||||
| @ -615,9 +615,8 @@ bool VariableDeclaration::isEventParameter() const | ||||
| 
 | ||||
| bool VariableDeclaration::hasReferenceOrMappingType() const | ||||
| { | ||||
| 	solAssert(typeName(), ""); | ||||
| 	solAssert(typeName()->annotation().type, "Can only be called after reference resolution"); | ||||
| 	Type const* type = typeName()->annotation().type; | ||||
| 	solAssert(typeName().annotation().type, "Can only be called after reference resolution"); | ||||
| 	Type const* type = typeName().annotation().type; | ||||
| 	return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type); | ||||
| } | ||||
| 
 | ||||
| @ -630,7 +629,12 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c | ||||
| 	else if (isCallableOrCatchParameter()) | ||||
| 	{ | ||||
| 		set<Location> locations{ Location::Memory }; | ||||
| 		if (isInternalCallableParameter() || isLibraryFunctionParameter() || isTryCatchParameter()) | ||||
| 		if ( | ||||
| 			isConstructorParameter() || | ||||
| 			isInternalCallableParameter() || | ||||
| 			isLibraryFunctionParameter() || | ||||
| 			isTryCatchParameter() | ||||
| 		) | ||||
| 			locations.insert(Location::Storage); | ||||
| 		if (!isTryCatchParameter() && !isConstructorParameter()) | ||||
| 			locations.insert(Location::CallData); | ||||
| @ -638,22 +642,8 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c | ||||
| 		return locations; | ||||
| 	} | ||||
| 	else if (isLocalVariable()) | ||||
| 	{ | ||||
| 		solAssert(typeName(), ""); | ||||
| 		auto dataLocations = [](TypePointer _type, auto&& _recursion) -> set<Location> { | ||||
| 			solAssert(_type, "Can only be called after reference resolution"); | ||||
| 			switch (_type->category()) | ||||
| 			{ | ||||
| 				case Type::Category::Array: | ||||
| 					return _recursion(dynamic_cast<ArrayType const*>(_type)->baseType(), _recursion); | ||||
| 				case Type::Category::Mapping: | ||||
| 					return set<Location>{ Location::Storage }; | ||||
| 				default: | ||||
| 					return set<Location>{ Location::Memory, Location::Storage, Location::CallData }; | ||||
| 			} | ||||
| 		}; | ||||
| 		return dataLocations(typeName()->annotation().type, dataLocations); | ||||
| 	} | ||||
| 		// Further restrictions will be imposed later on.
 | ||||
| 		return set<Location>{ Location::Memory, Location::Storage, Location::CallData }; | ||||
| 	else | ||||
| 		// Struct members etc.
 | ||||
| 		return set<Location>{ Location::Unspecified }; | ||||
|  | ||||
| @ -506,8 +506,6 @@ public: | ||||
| 
 | ||||
| 	/// Returns the constructor or nullptr if no constructor was specified.
 | ||||
| 	FunctionDefinition const* constructor() const; | ||||
| 	/// @returns true iff the constructor of this contract is public (or non-existing).
 | ||||
| 	bool constructorIsPublic() const; | ||||
| 	/// @returns true iff the contract can be deployed, i.e. is not abstract and has a
 | ||||
| 	/// public constructor.
 | ||||
| 	/// Should only be called after the type checker has run.
 | ||||
| @ -809,15 +807,16 @@ public: | ||||
| 	bool isPayable() const { return m_stateMutability == StateMutability::Payable; } | ||||
| 	std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } | ||||
| 	Block const& body() const { solAssert(m_body, ""); return *m_body; } | ||||
| 	Visibility defaultVisibility() const override; | ||||
| 	bool isVisibleInContract() const override | ||||
| 	{ | ||||
| 		return Declaration::isVisibleInContract() && isOrdinary(); | ||||
| 		return isOrdinary() && Declaration::isVisibleInContract(); | ||||
| 	} | ||||
| 	bool isVisibleViaContractTypeAccess() const override | ||||
| 	{ | ||||
| 		return visibility() >= Visibility::Public; | ||||
| 		return isOrdinary() && visibility() >= Visibility::Public; | ||||
| 	} | ||||
| 	bool isPartOfExternalInterface() const override { return isPublic() && isOrdinary(); } | ||||
| 	bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); } | ||||
| 
 | ||||
| 	/// @returns the external signature of the function
 | ||||
| 	/// That consists of the name of the function followed by the types of the
 | ||||
| @ -897,13 +896,16 @@ public: | ||||
| 		m_isIndexed(_isIndexed), | ||||
| 		m_mutability(_mutability), | ||||
| 		m_overrides(std::move(_overrides)), | ||||
| 		m_location(_referenceLocation) {} | ||||
| 		m_location(_referenceLocation) | ||||
| 	{ | ||||
| 		solAssert(m_typeName, ""); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	void accept(ASTVisitor& _visitor) override; | ||||
| 	void accept(ASTConstVisitor& _visitor) const override; | ||||
| 
 | ||||
| 	TypeName* typeName() const { return m_typeName.get(); } | ||||
| 	TypeName const& typeName() const { return *m_typeName; } | ||||
| 	ASTPointer<Expression> const& value() const { return m_value; } | ||||
| 
 | ||||
| 	bool isLValue() const override; | ||||
| @ -929,6 +931,7 @@ public: | ||||
| 	/// @returns true if this variable is a parameter or return parameter of an internal function
 | ||||
| 	/// or a function type of internal visibility.
 | ||||
| 	bool isInternalCallableParameter() const; | ||||
| 	/// @returns true if this variable is the parameter of a constructor.
 | ||||
| 	bool isConstructorParameter() const; | ||||
| 	/// @returns true iff this variable is a parameter(or return parameter of a library function
 | ||||
| 	bool isLibraryFunctionParameter() const; | ||||
| @ -965,7 +968,7 @@ protected: | ||||
| 	Visibility defaultVisibility() const override { return Visibility::Internal; } | ||||
| 
 | ||||
| private: | ||||
| 	ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
 | ||||
| 	ASTPointer<TypeName> m_typeName; | ||||
| 	/// Initially assigned value, can be missing. For local variables, this is stored inside
 | ||||
| 	/// VariableDeclarationStatement and not here.
 | ||||
| 	ASTPointer<Expression> m_value; | ||||
| @ -2087,8 +2090,6 @@ public: | ||||
| 		None = static_cast<int>(Token::Illegal), | ||||
| 		Wei = static_cast<int>(Token::SubWei), | ||||
| 		Gwei = static_cast<int>(Token::SubGwei), | ||||
| 		Szabo = static_cast<int>(Token::SubSzabo), | ||||
| 		Finney = static_cast<int>(Token::SubFinney), | ||||
| 		Ether = static_cast<int>(Token::SubEther), | ||||
| 		Second = static_cast<int>(Token::SubSecond), | ||||
| 		Minute = static_cast<int>(Token::SubMinute), | ||||
|  | ||||
| @ -139,6 +139,9 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation | ||||
| 	/// recursion immediately raises an error.
 | ||||
| 	/// Will be filled in by the DeclarationTypeChecker.
 | ||||
| 	std::optional<bool> recursive; | ||||
| 	/// Whether the struct contains a mapping type, either directly or, indirectly inside another
 | ||||
| 	/// struct or an array.
 | ||||
| 	std::optional<bool> containsNestedMapping; | ||||
| }; | ||||
| 
 | ||||
| struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation | ||||
|  | ||||
| @ -203,7 +203,7 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie | ||||
| 
 | ||||
| void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node) | ||||
| { | ||||
| 	_stream << util::jsonPrettyPrint(toJson(_node)); | ||||
| 	_stream << util::jsonPrettyPrint(util::removeNullMembers(toJson(_node))); | ||||
| } | ||||
| 
 | ||||
| Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node) | ||||
| @ -351,12 +351,18 @@ bool ASTJsonConverter::visit(OverrideSpecifier const& _node) | ||||
| 
 | ||||
| bool ASTJsonConverter::visit(FunctionDefinition const& _node) | ||||
| { | ||||
| 	Visibility visibility; | ||||
| 	if (_node.isConstructor()) | ||||
| 		visibility = _node.annotation().contract->abstract() ? Visibility::Internal : Visibility::Public; | ||||
| 	else | ||||
| 		visibility = _node.visibility(); | ||||
| 
 | ||||
| 	std::vector<pair<string, Json::Value>> 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("stateMutability", stateMutabilityToString(_node.stateMutability())), | ||||
| 		make_pair("visibility", Declaration::visibilityToString(_node.visibility())), | ||||
| 		make_pair("visibility", Declaration::visibilityToString(visibility)), | ||||
| 		make_pair("virtual", _node.markedVirtual()), | ||||
| 		make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), | ||||
| 		make_pair("parameters", toJson(_node.parameterList())), | ||||
| @ -366,6 +372,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) | ||||
| 		make_pair("implemented", _node.isImplemented()), | ||||
| 		make_pair("scope", idOrNull(_node.scope())) | ||||
| 	}; | ||||
| 
 | ||||
| 	if (_node.isPartOfExternalInterface()) | ||||
| 		attributes.emplace_back("functionSelector", _node.externalIdentifierHex()); | ||||
| 	if (!_node.annotation().baseFunctions.empty()) | ||||
| @ -380,7 +387,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node) | ||||
| { | ||||
| 	std::vector<pair<string, Json::Value>> attributes = { | ||||
| 		make_pair("name", _node.name()), | ||||
| 		make_pair("typeName", toJsonOrNull(_node.typeName())), | ||||
| 		make_pair("typeName", toJson(_node.typeName())), | ||||
| 		make_pair("constant", _node.isConstant()), | ||||
| 		make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())), | ||||
| 		make_pair("stateVariable", _node.isStateVariable()), | ||||
| @ -912,8 +919,11 @@ string ASTJsonConverter::literalTokenKind(Token _token) | ||||
| 	case Token::Number: | ||||
| 		return "number"; | ||||
| 	case Token::StringLiteral: | ||||
| 	case Token::HexStringLiteral: | ||||
| 		return "string"; | ||||
| 	case Token::UnicodeStringLiteral: | ||||
| 		return "unicodeString"; | ||||
| 	case Token::HexStringLiteral: | ||||
| 		return "hexString"; | ||||
| 	case Token::TrueLiteral: | ||||
| 	case Token::FalseLiteral: | ||||
| 		return "bool"; | ||||
|  | ||||
| @ -400,7 +400,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V | ||||
| 	return createASTNode<FunctionDefinition>( | ||||
| 		_node, | ||||
| 		memberAsASTString(_node, "name"), | ||||
| 		visibility(_node), | ||||
| 		kind == Token::Constructor ? Visibility::Default : visibility(_node), | ||||
| 		stateMutability(_node), | ||||
| 		kind, | ||||
| 		memberAsBool(_node, "virtual"), | ||||
| @ -886,7 +886,8 @@ ASTPointer<StructuredDocumentation> ASTJsonImporter::createDocumentation(Json::V | ||||
| 
 | ||||
| Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name) | ||||
| { | ||||
| 	astAssert(_node.isMember(_name), "Node '" + _node["nodeType"].asString() + "' (id " + _node["id"].asString() + ") is missing field '" + _name + "'."); | ||||
| 	if (!_node.isMember(_name)) | ||||
| 		return Json::nullValue; | ||||
| 	return _node[_name]; | ||||
| } | ||||
| 
 | ||||
| @ -942,6 +943,10 @@ Token ASTJsonImporter::literalTokenKind(Json::Value const& _node) | ||||
| 		tok = Token::Number; | ||||
| 	else if (_node["kind"].asString() == "string") | ||||
| 		tok = Token::StringLiteral; | ||||
| 	else if (_node["kind"].asString() == "unicodeString") | ||||
| 		tok = Token::UnicodeStringLiteral; | ||||
| 	else if (_node["kind"].asString() == "hexString") | ||||
| 		tok = Token::HexStringLiteral; | ||||
| 	else if (_node["kind"].asString() == "bool") | ||||
| 		tok = (member(_node, "value").asString() == "true") ? Token::TrueLiteral : Token::FalseLiteral; | ||||
| 	else | ||||
| @ -1004,10 +1009,6 @@ Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _no | ||||
| 		return Literal::SubDenomination::Wei; | ||||
| 	else if (subDenStr == "gwei") | ||||
| 		return Literal::SubDenomination::Gwei; | ||||
| 	else if (subDenStr == "szabo") | ||||
| 		return Literal::SubDenomination::Szabo; | ||||
| 	else if (subDenStr == "finney") | ||||
| 		return Literal::SubDenomination::Finney; | ||||
| 	else if (subDenStr == "ether") | ||||
| 		return Literal::SubDenomination::Ether; | ||||
| 	else if (subDenStr == "seconds") | ||||
|  | ||||
| @ -64,7 +64,8 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) | ||||
| 
 | ||||
| Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _name) | ||||
| { | ||||
| 	astAssert(_node.isMember(_name), "Node is missing field '" + _name + "'."); | ||||
| 	if (!_node.isMember(_name)) | ||||
| 		return Json::nullValue; | ||||
| 	return _node[_name]; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -349,6 +349,7 @@ TypePointer TypeProvider::forLiteral(Literal const& _literal) | ||||
| 	case Token::Number: | ||||
| 		return rationalNumber(_literal); | ||||
| 	case Token::StringLiteral: | ||||
| 	case Token::UnicodeStringLiteral: | ||||
| 	case Token::HexStringLiteral: | ||||
| 		return stringLiteral(_literal.value()); | ||||
| 	default: | ||||
|  | ||||
| @ -97,6 +97,7 @@ public: | ||||
| 	static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); } | ||||
| 
 | ||||
| 	static IntegerType const* uint256() { return uint(256); } | ||||
| 	static IntegerType const* int256() { return integer(256, IntegerType::Modifier::Signed); } | ||||
| 
 | ||||
| 	static FixedPointType const* fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier); | ||||
| 
 | ||||
|  | ||||
| @ -417,11 +417,9 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc | ||||
| 	if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope)) | ||||
| 		usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes()); | ||||
| 	else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope)) | ||||
| 	{ | ||||
| 		for (ContractDefinition const* contract: contract->annotation().linearizedBaseContracts) | ||||
| 			usingForDirectives += contract->usingForDirectives(); | ||||
| 		usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes()); | ||||
| 	} | ||||
| 		usingForDirectives += | ||||
| 			contract->usingForDirectives() + | ||||
| 			ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes()); | ||||
| 	else | ||||
| 		solAssert(false, ""); | ||||
| 
 | ||||
| @ -570,7 +568,7 @@ bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType) | ||||
| 	if (_operator == Token::SHR) | ||||
| 		return false; | ||||
| 	else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType)) | ||||
| 		return true; | ||||
| 		return !otherInt->isSigned(); | ||||
| 	else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType)) | ||||
| 		return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); | ||||
| 	else | ||||
| @ -954,12 +952,6 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal | ||||
| 		case Literal::SubDenomination::Gwei: | ||||
| 			value *= bigint("1000000000"); | ||||
| 			break; | ||||
| 		case Literal::SubDenomination::Szabo: | ||||
| 			value *= bigint("1000000000000"); | ||||
| 			break; | ||||
| 		case Literal::SubDenomination::Finney: | ||||
| 			value *= bigint("1000000000000000"); | ||||
| 			break; | ||||
| 		case Literal::SubDenomination::Ether: | ||||
| 			value *= bigint("1000000000000000000"); | ||||
| 			break; | ||||
| @ -1057,10 +1049,38 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* | ||||
| { | ||||
| 	if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) | ||||
| 	{ | ||||
| 		auto commonType = Type::commonType(this, _other); | ||||
| 		if (!commonType) | ||||
| 			return nullptr; | ||||
| 		return commonType->binaryOperatorResult(_operator, _other); | ||||
| 		if (isFractional()) | ||||
| 			return TypeResult::err("Fractional literals not supported."); | ||||
| 		else if (!integerType()) | ||||
| 			return TypeResult::err("Literal too large."); | ||||
| 
 | ||||
| 		// Shift and exp are not symmetric, so it does not make sense to swap
 | ||||
| 		// the types as below. As an exception, we always use uint here.
 | ||||
| 		if (TokenTraits::isShiftOp(_operator)) | ||||
| 		{ | ||||
| 			if (!isValidShiftAndAmountType(_operator, *_other)) | ||||
| 				return nullptr; | ||||
| 			return isNegative() ? TypeProvider::int256() : TypeProvider::uint256(); | ||||
| 		} | ||||
| 		else if (Token::Exp == _operator) | ||||
| 		{ | ||||
| 			if (auto const* otherIntType = dynamic_cast<IntegerType const*>(_other)) | ||||
| 			{ | ||||
| 				if (otherIntType->isSigned()) | ||||
| 					return TypeResult::err("Exponentiation power is not allowed to be a signed integer type."); | ||||
| 			} | ||||
| 			else if (dynamic_cast<FixedPointType const*>(_other)) | ||||
| 				return TypeResult::err("Exponent is fractional."); | ||||
| 
 | ||||
| 			return isNegative() ? TypeProvider::int256() : TypeProvider::uint256(); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			auto commonType = Type::commonType(this, _other); | ||||
| 			if (!commonType) | ||||
| 				return nullptr; | ||||
| 			return commonType->binaryOperatorResult(_operator, _other); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (_other->category() != category()) | ||||
| 		return nullptr; | ||||
| @ -1388,7 +1408,7 @@ BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) | ||||
| 		return | ||||
| 			arrayType->isByteArray() && | ||||
| 			!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()) && | ||||
| 			!(arrayType->isString() && !isValidUTF8()); | ||||
| 			!(arrayType->isString() && !util::validateUTF8(value())); | ||||
| 	else | ||||
| 		return false; | ||||
| } | ||||
| @ -1422,11 +1442,6 @@ TypePointer StringLiteralType::mobileType() const | ||||
| 	return TypeProvider::stringMemory(); | ||||
| } | ||||
| 
 | ||||
| bool StringLiteralType::isValidUTF8() const | ||||
| { | ||||
| 	return util::validateUTF8(m_value); | ||||
| } | ||||
| 
 | ||||
| FixedBytesType::FixedBytesType(unsigned _bytes): m_bytes(_bytes) | ||||
| { | ||||
| 	solAssert( | ||||
| @ -2032,6 +2047,20 @@ TypeResult ArrayType::interfaceType(bool _inLibrary) const | ||||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| Type const* ArrayType::finalBaseType(bool _breakIfDynamicArrayType) const | ||||
| { | ||||
| 	Type const* finalBaseType = this; | ||||
| 
 | ||||
| 	while (auto arrayType = dynamic_cast<ArrayType const*>(finalBaseType)) | ||||
| 	{ | ||||
| 		if (_breakIfDynamicArrayType && arrayType->isDynamicallySized()) | ||||
| 			break; | ||||
| 		finalBaseType = arrayType->baseType(); | ||||
| 	} | ||||
| 
 | ||||
| 	return finalBaseType; | ||||
| } | ||||
| 
 | ||||
| u256 ArrayType::memoryDataSize() const | ||||
| { | ||||
| 	solAssert(!isDynamicallySized(), ""); | ||||
| @ -2260,7 +2289,7 @@ unsigned StructType::calldataEncodedSize(bool) const | ||||
| 	unsigned size = 0; | ||||
| 	for (auto const& member: members(nullptr)) | ||||
| 	{ | ||||
| 		solAssert(member.type->canLiveOutsideStorage(), ""); | ||||
| 		solAssert(!member.type->containsNestedMapping(), ""); | ||||
| 		// Struct members are always padded.
 | ||||
| 		size += member.type->calldataEncodedSize(); | ||||
| 	} | ||||
| @ -2275,7 +2304,7 @@ unsigned StructType::calldataEncodedTailSize() const | ||||
| 	unsigned size = 0; | ||||
| 	for (auto const& member: members(nullptr)) | ||||
| 	{ | ||||
| 		solAssert(member.type->canLiveOutsideStorage(), ""); | ||||
| 		solAssert(!member.type->containsNestedMapping(), ""); | ||||
| 		// Struct members are always padded.
 | ||||
| 		size += member.type->calldataHeadSize(); | ||||
| 	} | ||||
| @ -2287,7 +2316,7 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const | ||||
| 	unsigned offset = 0; | ||||
| 	for (auto const& member: members(nullptr)) | ||||
| 	{ | ||||
| 		solAssert(member.type->canLiveOutsideStorage(), ""); | ||||
| 		solAssert(!member.type->containsNestedMapping(), ""); | ||||
| 		if (member.name == _member) | ||||
| 			return offset; | ||||
| 		// Struct members are always padded.
 | ||||
| @ -2332,6 +2361,42 @@ u256 StructType::storageSize() const | ||||
| 	return max<u256>(1, members(nullptr).storageSize()); | ||||
| } | ||||
| 
 | ||||
| bool StructType::containsNestedMapping() const | ||||
| { | ||||
| 	if (!m_struct.annotation().containsNestedMapping.has_value()) | ||||
| 	{ | ||||
| 		bool hasNestedMapping = false; | ||||
| 
 | ||||
| 		util::BreadthFirstSearch<StructDefinition const*> breadthFirstSearch{{&m_struct}}; | ||||
| 
 | ||||
| 		breadthFirstSearch.run( | ||||
| 			[&](StructDefinition const* _struct, auto&& _addChild) | ||||
| 			{ | ||||
| 				for (auto const& member: _struct->members()) | ||||
| 				{ | ||||
| 					TypePointer memberType = member->annotation().type; | ||||
| 					solAssert(memberType, ""); | ||||
| 
 | ||||
| 					if (auto arrayType = dynamic_cast<ArrayType const*>(memberType)) | ||||
| 						memberType = arrayType->finalBaseType(false); | ||||
| 
 | ||||
| 					if (dynamic_cast<MappingType const*>(memberType)) | ||||
| 					{ | ||||
| 						hasNestedMapping = true; | ||||
| 						breadthFirstSearch.abort(); | ||||
| 					} | ||||
| 					else if (auto structType = dynamic_cast<StructType const*>(memberType)) | ||||
| 						_addChild(&structType->structDefinition()); | ||||
| 				} | ||||
| 
 | ||||
| 			}); | ||||
| 
 | ||||
| 		m_struct.annotation().containsNestedMapping = hasNestedMapping; | ||||
| 	} | ||||
| 
 | ||||
| 	return m_struct.annotation().containsNestedMapping.value(); | ||||
| } | ||||
| 
 | ||||
| string StructType::toString(bool _short) const | ||||
| { | ||||
| 	string ret = "struct " + m_struct.annotation().canonicalName; | ||||
| @ -2347,10 +2412,7 @@ MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const | ||||
| 	{ | ||||
| 		TypePointer type = variable->annotation().type; | ||||
| 		solAssert(type, ""); | ||||
| 		// If we are not in storage, skip all members that cannot live outside of storage,
 | ||||
| 		// ex. mappings and array of mappings
 | ||||
| 		if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) | ||||
| 			continue; | ||||
| 		solAssert(!(location() != DataLocation::Storage && type->containsNestedMapping()), ""); | ||||
| 		members.emplace_back( | ||||
| 			variable->name(), | ||||
| 			copyForLocationIfReference(type), | ||||
| @ -2519,10 +2581,9 @@ FunctionTypePointer StructType::constructorType() const | ||||
| { | ||||
| 	TypePointers paramTypes; | ||||
| 	strings paramNames; | ||||
| 	solAssert(!containsNestedMapping(), ""); | ||||
| 	for (auto const& member: members(nullptr)) | ||||
| 	{ | ||||
| 		if (!member.type->canLiveOutsideStorage()) | ||||
| 			continue; | ||||
| 		paramNames.push_back(member.name); | ||||
| 		paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type)); | ||||
| 	} | ||||
| @ -2556,20 +2617,12 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const | ||||
| 
 | ||||
| TypePointers StructType::memoryMemberTypes() const | ||||
| { | ||||
| 	solAssert(!containsNestedMapping(), ""); | ||||
| 	TypePointers types; | ||||
| 	for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) | ||||
| 		if (variable->annotation().type->canLiveOutsideStorage()) | ||||
| 			types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type)); | ||||
| 	return types; | ||||
| } | ||||
| 		types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type)); | ||||
| 
 | ||||
| set<string> StructType::membersMissingInMemory() const | ||||
| { | ||||
| 	set<string> missing; | ||||
| 	for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) | ||||
| 		if (!variable->annotation().type->canLiveOutsideStorage()) | ||||
| 			missing.insert(variable->name()); | ||||
| 	return missing; | ||||
| 	return types; | ||||
| } | ||||
| 
 | ||||
| vector<tuple<string, TypePointer>> StructType::makeStackItems() const | ||||
| @ -3183,7 +3236,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackItems() const | ||||
| 	case Kind::DelegateCall: | ||||
| 		slots = { | ||||
| 			make_tuple("address", TypeProvider::address()), | ||||
| 			make_tuple("functionIdentifier", TypeProvider::uint(32)) | ||||
| 			make_tuple("functionSelector", TypeProvider::uint(32)) | ||||
| 		}; | ||||
| 		break; | ||||
| 	case Kind::BareCall: | ||||
| @ -3367,6 +3420,28 @@ TypeResult FunctionType::interfaceType(bool /*_inLibrary*/) const | ||||
| 		return TypeResult::err("Internal type is not allowed for public or external functions."); | ||||
| } | ||||
| 
 | ||||
| TypePointer FunctionType::mobileType() const | ||||
| { | ||||
| 	if (m_valueSet || m_gasSet || m_saltSet || m_bound) | ||||
| 		return nullptr; | ||||
| 
 | ||||
| 	// return function without parameter names
 | ||||
| 	return TypeProvider::function( | ||||
| 		m_parameterTypes, | ||||
| 		m_returnParameterTypes, | ||||
| 		strings(m_parameterTypes.size()), | ||||
| 		strings(m_returnParameterNames.size()), | ||||
| 		m_kind, | ||||
| 		m_arbitraryParameters, | ||||
| 		m_stateMutability, | ||||
| 		m_declaration, | ||||
| 		m_gasSet, | ||||
| 		m_valueSet, | ||||
| 		m_bound, | ||||
| 		m_saltSet | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| bool FunctionType::canTakeArguments( | ||||
| 	FuncCallArguments const& _arguments, | ||||
| 	Type const* _selfType | ||||
| @ -3716,7 +3791,10 @@ TypeResult MappingType::interfaceType(bool _inLibrary) const | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 		return TypeResult::err("Only libraries are allowed to use the mapping type in public or external functions."); | ||||
| 		return TypeResult::err( | ||||
| 			"Types containing (nested) mappings can only be parameters or " | ||||
| 			"return variables of internal or library functions." | ||||
| 		); | ||||
| 
 | ||||
| 	return this; | ||||
| } | ||||
|  | ||||
| @ -271,7 +271,11 @@ public: | ||||
| 	/// Returns true if the type can be stored in storage.
 | ||||
| 	virtual bool canBeStored() const { return true; } | ||||
| 	/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
 | ||||
| 	virtual bool canLiveOutsideStorage() const { return true; } | ||||
| 	virtual bool containsNestedMapping() const | ||||
| 	{ | ||||
| 		solAssert(nameable(), "Called for a non nameable type."); | ||||
| 		return false; | ||||
| 	} | ||||
| 	/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
 | ||||
| 	/// i.e. it behaves differently in lvalue context and in value context.
 | ||||
| 	virtual bool isValueType() const { return false; } | ||||
| @ -561,7 +565,6 @@ public: | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| 
 | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 
 | ||||
| 	std::string toString(bool _short) const override; | ||||
| 	u256 literalValue(Literal const* _literal) const override; | ||||
| @ -622,13 +625,10 @@ public: | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| 
 | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 
 | ||||
| 	std::string toString(bool) const override; | ||||
| 	TypePointer mobileType() const override; | ||||
| 
 | ||||
| 	bool isValidUTF8() const; | ||||
| 
 | ||||
| 	std::string const& value() const { return m_value; } | ||||
| 
 | ||||
| protected: | ||||
| @ -794,8 +794,9 @@ public: | ||||
| 	bool isDynamicallyEncoded() const override; | ||||
| 	bigint storageSizeUpperBound() const override; | ||||
| 	u256 storageSize() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); } | ||||
| 	bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); } | ||||
| 	bool nameable() const override { return true; } | ||||
| 
 | ||||
| 	std::string toString(bool _short) const override; | ||||
| 	std::string canonicalName() const override; | ||||
| 	std::string signatureInExternalFunction(bool _structsByName) const override; | ||||
| @ -811,6 +812,7 @@ public: | ||||
| 	/// @returns true if this is a string
 | ||||
| 	bool isString() const { return m_arrayKind == ArrayKind::String; } | ||||
| 	Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; } | ||||
| 	Type const* finalBaseType(bool breakIfDynamicArrayType) const; | ||||
| 	u256 const& length() const { return m_length; } | ||||
| 	u256 memoryDataSize() const override; | ||||
| 
 | ||||
| @ -855,7 +857,6 @@ public: | ||||
| 	unsigned calldataEncodedTailSize() const override { return 32; } | ||||
| 	bool isDynamicallySized() const override { return true; } | ||||
| 	bool isDynamicallyEncoded() const override { return true; } | ||||
| 	bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); } | ||||
| 	std::string toString(bool _short) const override; | ||||
| 	TypePointer mobileType() const override; | ||||
| 
 | ||||
| @ -896,7 +897,6 @@ public: | ||||
| 	} | ||||
| 	unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } | ||||
| 	bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return !isSuper(); } | ||||
| 	bool isValueType() const override { return !isSuper(); } | ||||
| 	bool nameable() const override { return !isSuper(); } | ||||
| 	std::string toString(bool _short) const override; | ||||
| @ -959,7 +959,7 @@ public: | ||||
| 	u256 memoryDataSize() const override; | ||||
| 	bigint storageSizeUpperBound() const override; | ||||
| 	u256 storageSize() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return true; } | ||||
| 	bool containsNestedMapping() const override; | ||||
| 	bool nameable() const override { return true; } | ||||
| 	std::string toString(bool _short) const override; | ||||
| 
 | ||||
| @ -989,8 +989,6 @@ public: | ||||
| 
 | ||||
| 	/// @returns the vector of types of members available in memory.
 | ||||
| 	TypePointers memoryMemberTypes() const; | ||||
| 	/// @returns the set of all members that are removed in the memory version (typically mappings).
 | ||||
| 	std::set<std::string> membersMissingInMemory() const; | ||||
| 
 | ||||
| 	void clearCache() const override; | ||||
| 
 | ||||
| @ -1021,7 +1019,6 @@ public: | ||||
| 	} | ||||
| 	unsigned storageBytes() const override; | ||||
| 	bool leftAligned() const override { return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return true; } | ||||
| 	std::string toString(bool _short) const override; | ||||
| 	std::string canonicalName() const override; | ||||
| 	bool isValueType() const override { return true; } | ||||
| @ -1061,7 +1058,6 @@ public: | ||||
| 	std::string toString(bool) const override; | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	u256 storageSize() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { return false; } | ||||
| 	TypePointer mobileType() const override; | ||||
| 	/// Converts components to their temporary types and performs some wildcard matching.
 | ||||
| @ -1232,11 +1228,11 @@ public: | ||||
| 	unsigned storageBytes() const override; | ||||
| 	bool isValueType() const override { return true; } | ||||
| 	bool nameable() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { return false; } | ||||
| 	MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; | ||||
| 	TypePointer encodingType() const override; | ||||
| 	TypeResult interfaceType(bool _inLibrary) const override; | ||||
| 	TypePointer mobileType() const override; | ||||
| 
 | ||||
| 	/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
 | ||||
| 	/// appropriate external types (i.e. the interfaceType()s) of input/return parameters of
 | ||||
| @ -1365,7 +1361,7 @@ public: | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| 	std::string toString(bool _short) const override; | ||||
| 	std::string canonicalName() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 	bool containsNestedMapping() const override { return true; } | ||||
| 	TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } | ||||
| 	Type const* encodingType() const override; | ||||
| 	TypeResult interfaceType(bool _inLibrary) const override; | ||||
| @ -1400,7 +1396,6 @@ public: | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	u256 storageSize() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } | ||||
| 	std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } | ||||
| 	MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; | ||||
| @ -1426,7 +1421,6 @@ public: | ||||
| 	TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	u256 storageSize() const override; | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } | ||||
| 	std::string richIdentifier() const override; | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| @ -1453,7 +1447,6 @@ public: | ||||
| 	std::string richIdentifier() const override; | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return true; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } | ||||
| 	MemberList::MemberMap nativeMembers(ASTNode const*) const override; | ||||
| 
 | ||||
| @ -1493,7 +1486,6 @@ public: | ||||
| 	std::string richIdentifier() const override; | ||||
| 	bool operator==(Type const& _other) const override; | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return true; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } | ||||
| 	MemberList::MemberMap nativeMembers(ASTNode const*) const override; | ||||
| 
 | ||||
| @ -1526,7 +1518,6 @@ public: | ||||
| 	TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } | ||||
| 	unsigned calldataEncodedSize(bool) const override { return 32; } | ||||
| 	bool canBeStored() const override { return false; } | ||||
| 	bool canLiveOutsideStorage() const override { return false; } | ||||
| 	bool isValueType() const override { return true; } | ||||
| 	bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } | ||||
| 	std::string toString(bool) const override { return "inaccessible dynamic type"; } | ||||
|  | ||||
| @ -861,8 +861,7 @@ string ABIFunctions::abiEncodingFunctionStruct( | ||||
| 		for (auto const& member: _to.members(nullptr)) | ||||
| 		{ | ||||
| 			solAssert(member.type, ""); | ||||
| 			if (!member.type->canLiveOutsideStorage()) | ||||
| 				continue; | ||||
| 			solAssert(!member.type->containsNestedMapping(), ""); | ||||
| 			TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false); | ||||
| 			solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); | ||||
| 			auto memberTypeFrom = _from.memberType(member.name); | ||||
| @ -1341,7 +1340,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr | ||||
| 		for (auto const& member: _type.members(nullptr)) | ||||
| 		{ | ||||
| 			solAssert(member.type, ""); | ||||
| 			solAssert(member.type->canLiveOutsideStorage(), ""); | ||||
| 			solAssert(!member.type->containsNestedMapping(), ""); | ||||
| 			auto decodingType = member.type->decodingType(); | ||||
| 			solAssert(decodingType, ""); | ||||
| 			bool dynamic = decodingType->isDynamicallyEncoded(); | ||||
|  | ||||
| @ -1080,8 +1080,7 @@ void CompilerUtils::convertType( | ||||
| 					// stack: <memory ptr> <source ref> <memory ptr>
 | ||||
| 					for (auto const& member: typeOnStack->members(nullptr)) | ||||
| 					{ | ||||
| 						if (!member.type->canLiveOutsideStorage()) | ||||
| 							continue; | ||||
| 						solAssert(!member.type->containsNestedMapping(), ""); | ||||
| 						pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name); | ||||
| 						_context << offsets.first << Instruction::DUP3 << Instruction::ADD; | ||||
| 						_context << u256(offsets.second); | ||||
|  | ||||
| @ -286,6 +286,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) | ||||
| 		m_currentLValue->storeValue(*rightIntermediateType, _assignment.location()); | ||||
| 	else  // compound assignment
 | ||||
| 	{ | ||||
| 		solAssert(binOp != Token::Exp, "Compound exp is not possible."); | ||||
| 		solAssert(leftType.isValueType(), "Compound operators only available for value types."); | ||||
| 		unsigned lvalueSize = m_currentLValue->sizeOnStack(); | ||||
| 		unsigned itemSize = _assignment.annotation().type->sizeOnStack(); | ||||
| @ -452,7 +453,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) | ||||
| 		bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op); | ||||
| 
 | ||||
| 		TypePointer leftTargetType = commonType; | ||||
| 		TypePointer rightTargetType = TokenTraits::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; | ||||
| 		TypePointer rightTargetType = | ||||
| 			TokenTraits::isShiftOp(c_op) || c_op == Token::Exp ? | ||||
| 			rightExpression.annotation().type->mobileType() : | ||||
| 			commonType; | ||||
| 		solAssert(rightTargetType, ""); | ||||
| 
 | ||||
| 		// for commutative operators, push the literal as late as possible to allow improved optimization
 | ||||
| @ -474,6 +478,8 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) | ||||
| 		if (TokenTraits::isShiftOp(c_op)) | ||||
| 			// shift only cares about the signedness of both sides
 | ||||
| 			appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType); | ||||
| 		else if (c_op == Token::Exp) | ||||
| 			appendExpOperatorCode(*leftTargetType, *rightTargetType); | ||||
| 		else if (TokenTraits::isCompareOp(c_op)) | ||||
| 			appendCompareOperatorCode(c_op, *commonType); | ||||
| 		else | ||||
| @ -1915,10 +1921,6 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) | ||||
| 			if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper()) | ||||
| 				m_context << Instruction::ADDRESS; | ||||
| 			break; | ||||
| 		case Type::Category::Integer: | ||||
| 			// "now"
 | ||||
| 			m_context << Instruction::TIMESTAMP; | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 		} | ||||
| @ -2084,9 +2086,6 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons | ||||
| 			m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD); | ||||
| 		break; | ||||
| 	} | ||||
| 	case Token::Exp: | ||||
| 		m_context << Instruction::EXP; | ||||
| 		break; | ||||
| 	default: | ||||
| 		solAssert(false, "Unknown arithmetic operator."); | ||||
| 	} | ||||
| @ -2121,7 +2120,6 @@ void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _v | ||||
| 		solAssert(dynamic_cast<FixedBytesType const*>(&_valueType), "Only integer and fixed bytes type supported for shifts."); | ||||
| 
 | ||||
| 	// The amount can be a RationalNumberType too.
 | ||||
| 	bool c_amountSigned = false; | ||||
| 	if (auto amountType = dynamic_cast<RationalNumberType const*>(&_shiftAmountType)) | ||||
| 	{ | ||||
| 		// This should be handled by the type checker.
 | ||||
| @ -2129,17 +2127,10 @@ void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _v | ||||
| 		solAssert(!amountType->integerType()->isSigned(), ""); | ||||
| 	} | ||||
| 	else if (auto amountType = dynamic_cast<IntegerType const*>(&_shiftAmountType)) | ||||
| 		c_amountSigned = amountType->isSigned(); | ||||
| 		solAssert(!amountType->isSigned(), ""); | ||||
| 	else | ||||
| 		solAssert(false, "Invalid shift amount type."); | ||||
| 
 | ||||
| 	// shift by negative amount throws exception
 | ||||
| 	if (c_amountSigned) | ||||
| 	{ | ||||
| 		m_context << u256(0) << Instruction::DUP3 << Instruction::SLT; | ||||
| 		m_context.appendConditionalInvalid(); | ||||
| 	} | ||||
| 
 | ||||
| 	m_context << Instruction::SWAP1; | ||||
| 	// stack: value_to_shift shift_amount
 | ||||
| 
 | ||||
| @ -2189,6 +2180,14 @@ void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _v | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type const& _exponentType) | ||||
| { | ||||
| 	solAssert(_valueType.category() == Type::Category::Integer, ""); | ||||
| 	solAssert(!dynamic_cast<IntegerType const&>(_exponentType).isSigned(), ""); | ||||
| 
 | ||||
| 	m_context << Instruction::EXP; | ||||
| } | ||||
| 
 | ||||
| void ExpressionCompiler::appendExternalFunctionCall( | ||||
| 	FunctionType const& _functionType, | ||||
| 	vector<ASTPointer<Expression const>> const& _arguments, | ||||
|  | ||||
| @ -101,6 +101,7 @@ private: | ||||
| 	void appendArithmeticOperatorCode(Token _operator, Type const& _type); | ||||
| 	void appendBitOperatorCode(Token _operator); | ||||
| 	void appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType); | ||||
| 	void appendExpOperatorCode(Type const& _valueType, Type const& _exponentType); | ||||
| 	/// @}
 | ||||
| 
 | ||||
| 	/// Appends code to call a function of the given type with the given arguments.
 | ||||
|  | ||||
| @ -356,13 +356,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc | ||||
| 				structType.structDefinition() == sourceType.structDefinition(), | ||||
| 				"Struct assignment with conversion." | ||||
| 			); | ||||
| 			solAssert(!structType.containsNestedMapping(), ""); | ||||
| 			solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); | ||||
| 			for (auto const& member: structType.members(nullptr)) | ||||
| 			{ | ||||
| 				// assign each member that can live outside of storage
 | ||||
| 				TypePointer const& memberType = member.type; | ||||
| 				if (!memberType->canLiveOutsideStorage()) | ||||
| 					continue; | ||||
| 				solAssert(memberType->nameable(), ""); | ||||
| 				TypePointer sourceMemberType = sourceType.memberType(member.name); | ||||
| 				if (sourceType.location() == DataLocation::Storage) | ||||
| 				{ | ||||
|  | ||||
| @ -347,20 +347,17 @@ string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _ | ||||
| { | ||||
| 	solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, ""); | ||||
| 	solAssert(_amountType.category() == Type::Category::Integer, ""); | ||||
| 	solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), ""); | ||||
| 	string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier(); | ||||
| 	return m_functionCollector.createFunction(functionName, [&]() { | ||||
| 		return | ||||
| 			Whiskers(R"( | ||||
| 			function <functionName>(value, bits) -> result { | ||||
| 				bits := <cleanAmount>(bits) | ||||
| 				<?amountSigned> | ||||
| 					if slt(bits, 0) { invalid() } | ||||
| 				</amountSigned> | ||||
| 				result := <cleanup>(<shift>(bits, value)) | ||||
| 			} | ||||
| 			)") | ||||
| 			("functionName", functionName) | ||||
| 			("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned()) | ||||
| 			("cleanAmount", cleanupFunction(_amountType)) | ||||
| 			("shift", shiftLeftFunctionDynamic()) | ||||
| 			("cleanup", cleanupFunction(_type)) | ||||
| @ -372,6 +369,7 @@ string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& | ||||
| { | ||||
| 	solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, ""); | ||||
| 	solAssert(_amountType.category() == Type::Category::Integer, ""); | ||||
| 	solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), ""); | ||||
| 	IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type); | ||||
| 	bool valueSigned = integerType && integerType->isSigned(); | ||||
| 
 | ||||
| @ -381,14 +379,10 @@ string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& | ||||
| 			Whiskers(R"( | ||||
| 			function <functionName>(value, bits) -> result { | ||||
| 				bits := <cleanAmount>(bits) | ||||
| 				<?amountSigned> | ||||
| 					if slt(bits, 0) { invalid() } | ||||
| 				</amountSigned> | ||||
| 				result := <cleanup>(<shift>(bits, <cleanup>(value))) | ||||
| 			} | ||||
| 			)") | ||||
| 			("functionName", functionName) | ||||
| 			("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned()) | ||||
| 			("cleanAmount", cleanupFunction(_amountType)) | ||||
| 			("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic()) | ||||
| 			("cleanup", cleanupFunction(_type)) | ||||
| @ -2412,6 +2406,29 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const | ||||
| 			.render(); | ||||
| 		} | ||||
| 
 | ||||
| 		if (_from.category() == Type::Category::Array && _to.category() == Type::Category::Array) | ||||
| 		{ | ||||
| 			auto const& fromArrayType = dynamic_cast<ArrayType const&>(_from); | ||||
| 			auto const& toArrayType = dynamic_cast<ArrayType const&>(_to); | ||||
| 
 | ||||
| 			solAssert(!fromArrayType.baseType()->isDynamicallyEncoded(), ""); | ||||
| 			solUnimplementedAssert(fromArrayType.isByteArray() && toArrayType.isByteArray(), ""); | ||||
| 			solUnimplementedAssert(toArrayType.location() == DataLocation::Memory, ""); | ||||
| 			solUnimplementedAssert(fromArrayType.location() == DataLocation::CallData, ""); | ||||
| 			solUnimplementedAssert(toArrayType.isDynamicallySized(), ""); | ||||
| 
 | ||||
| 			Whiskers templ(R"( | ||||
| 				function <functionName>(offset, length) -> converted { | ||||
| 					converted := <allocateMemoryArray>(length) | ||||
| 					<copyToMemory>(offset, add(converted, 0x20), length) | ||||
| 				} | ||||
| 			)"); | ||||
| 			templ("functionName", functionName); | ||||
| 			templ("allocateMemoryArray", allocateMemoryArrayFunction(toArrayType)); | ||||
| 			templ("copyToMemory", copyToMemoryFunction(fromArrayType.location() == DataLocation::CallData)); | ||||
| 			return templ.render(); | ||||
| 		} | ||||
| 
 | ||||
| 		solUnimplementedAssert( | ||||
| 			_from.category() == Type::Category::StringLiteral, | ||||
| 			"Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented." | ||||
|  | ||||
| @ -1395,7 +1395,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | ||||
| 				solAssert(false, "Contract member is neither variable nor function."); | ||||
| 
 | ||||
| 			define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); | ||||
| 			define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(identifier) << "\n"; | ||||
| 			define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(identifier) << "\n"; | ||||
| 		} | ||||
| 		else | ||||
| 			solAssert(false, "Invalid member access in contract"); | ||||
| @ -1431,7 +1431,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | ||||
| 				*_memberAccess.expression().annotation().type | ||||
| 			); | ||||
| 			if (functionType.kind() == FunctionType::Kind::External) | ||||
| 				define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionIdentifier")); | ||||
| 				define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionSelector")); | ||||
| 			else if (functionType.kind() == FunctionType::Kind::Declaration) | ||||
| 			{ | ||||
| 				solAssert(functionType.hasDeclaration(), ""); | ||||
| @ -1683,7 +1683,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | ||||
| 					break; | ||||
| 				case FunctionType::Kind::DelegateCall: | ||||
| 					define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); | ||||
| 					define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n"; | ||||
| 					define(IRVariable(_memberAccess).part("functionSelector")) << formatNumber(memberFunctionType->externalIdentifier()) << "\n"; | ||||
| 					break; | ||||
| 				case FunctionType::Kind::External: | ||||
| 				case FunctionType::Kind::Creation: | ||||
| @ -2077,7 +2077,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | ||||
| 
 | ||||
| 		// storage for arguments and returned data
 | ||||
| 		let <pos> := <freeMemory> | ||||
| 		mstore(<pos>, <shl28>(<funId>)) | ||||
| 		mstore(<pos>, <shl28>(<funSel>)) | ||||
| 		let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>) | ||||
| 
 | ||||
| 		let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>) | ||||
| @ -2107,7 +2107,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | ||||
| 	templ("freeMemory", freeMemory()); | ||||
| 	templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); | ||||
| 
 | ||||
| 	templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); | ||||
| 	templ("funSel", IRVariable(_functionCall.expression()).part("functionSelector").name()); | ||||
| 	templ("address", IRVariable(_functionCall.expression()).part("address").name()); | ||||
| 
 | ||||
| 	// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
 | ||||
|  | ||||
| @ -29,6 +29,11 @@ | ||||
| #include <libsmtutil/CHCSmtLib2Interface.h> | ||||
| #include <libsolutil/Algorithms.h> | ||||
| 
 | ||||
| #include <boost/algorithm/string/join.hpp> | ||||
| #include <boost/range/adaptor/reversed.hpp> | ||||
| 
 | ||||
| #include <queue> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace solidity; | ||||
| using namespace solidity::util; | ||||
| @ -122,6 +127,7 @@ bool CHC::visit(ContractDefinition const& _contract) | ||||
| 	string suffix = _contract.name() + "_" + to_string(_contract.id()); | ||||
| 	m_errorPredicate = createSymbolicBlock(arity0FunctionSort(), "error_" + suffix); | ||||
| 	m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix); | ||||
| 	m_symbolFunction[m_constructorSummaryPredicate->currentFunctionValue().name] = &_contract; | ||||
| 	m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix); | ||||
| 	auto stateExprs = currentStateVariables(); | ||||
| 	setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); | ||||
| @ -142,12 +148,11 @@ void CHC::endVisit(ContractDefinition const& _contract) | ||||
| 	else | ||||
| 		inlineConstructorHierarchy(_contract); | ||||
| 
 | ||||
| 	auto summary = predicate(*m_constructorSummaryPredicate, vector<smtutil::Expression>{m_error.currentValue()} + currentStateVariables()); | ||||
| 	connectBlocks(m_currentBlock, summary); | ||||
| 	connectBlocks(m_currentBlock, summary(_contract), m_error.currentValue() == 0); | ||||
| 
 | ||||
| 	clearIndices(m_currentContract, nullptr); | ||||
| 	auto stateExprs = vector<smtutil::Expression>{m_error.currentValue()} + currentStateVariables(); | ||||
| 	setCurrentBlock(*m_constructorSummaryPredicate, &stateExprs); | ||||
| 	vector<smtutil::Expression> symbArgs = currentFunctionVariables(*m_currentContract); | ||||
| 	setCurrentBlock(*m_constructorSummaryPredicate, &symbArgs); | ||||
| 
 | ||||
| 	addAssertVerificationTarget(m_currentContract, m_currentBlock, smtutil::Expression(true), m_error.currentValue()); | ||||
| 	connectBlocks(m_currentBlock, interface(), m_error.currentValue() == 0); | ||||
| @ -209,6 +214,8 @@ void CHC::endVisit(FunctionDefinition const& _function) | ||||
| 	if (!_function.isImplemented()) | ||||
| 		return; | ||||
| 
 | ||||
| 	solAssert(m_currentFunction && m_currentContract, ""); | ||||
| 
 | ||||
| 	// This is the case for base constructor inlining.
 | ||||
| 	if (m_currentFunction != &_function) | ||||
| 	{ | ||||
| @ -228,10 +235,10 @@ void CHC::endVisit(FunctionDefinition const& _function) | ||||
| 		{ | ||||
| 			string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id()); | ||||
| 			auto constructorExit = createSymbolicBlock(constructorSort(), "constructor_exit_" + suffix); | ||||
| 			connectBlocks(m_currentBlock, predicate(*constructorExit, vector<smtutil::Expression>{m_error.currentValue()} + currentStateVariables())); | ||||
| 			connectBlocks(m_currentBlock, predicate(*constructorExit, currentFunctionVariables(*m_currentContract))); | ||||
| 
 | ||||
| 			clearIndices(m_currentContract, m_currentFunction); | ||||
| 			auto stateExprs = vector<smtutil::Expression>{m_error.currentValue()} + currentStateVariables(); | ||||
| 			auto stateExprs = currentFunctionVariables(*m_currentContract); | ||||
| 			setCurrentBlock(*constructorExit, &stateExprs); | ||||
| 		} | ||||
| 		else | ||||
| @ -564,6 +571,7 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) | ||||
| 			m_context.variable(*var)->increaseIndex(); | ||||
| 
 | ||||
| 	auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + currentStateVariables()); | ||||
| 	m_symbolFunction[nondet.name] = &_funCall; | ||||
| 	m_context.addAssertion(nondet); | ||||
| 
 | ||||
| 	m_context.addAssertion(m_error.currentValue() == 0); | ||||
| @ -613,6 +621,7 @@ void CHC::resetSourceAnalysis() | ||||
| 	m_errorIds.clear(); | ||||
| 	m_callGraph.clear(); | ||||
| 	m_summaries.clear(); | ||||
| 	m_symbolFunction.clear(); | ||||
| } | ||||
| 
 | ||||
| void CHC::resetContractAnalysis() | ||||
| @ -683,6 +692,11 @@ vector<VariableDeclaration const*> CHC::stateVariablesIncludingInheritedAndPriva | ||||
| 	); | ||||
| } | ||||
| 
 | ||||
| vector<VariableDeclaration const*> CHC::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) | ||||
| { | ||||
| 	return stateVariablesIncludingInheritedAndPrivate(dynamic_cast<ContractDefinition const&>(*_function.scope())); | ||||
| } | ||||
| 
 | ||||
| vector<smtutil::SortPointer> CHC::stateSorts(ContractDefinition const& _contract) | ||||
| { | ||||
| 	return applyMap( | ||||
| @ -693,6 +707,10 @@ vector<smtutil::SortPointer> CHC::stateSorts(ContractDefinition const& _contract | ||||
| 
 | ||||
| smtutil::SortPointer CHC::constructorSort() | ||||
| { | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	if (auto const* constructor = m_currentContract->constructor()) | ||||
| 		return sort(*constructor); | ||||
| 
 | ||||
| 	return make_shared<smtutil::FunctionSort>( | ||||
| 		vector<smtutil::SortPointer>{smtutil::SortProvider::uintSort} + m_stateSorts, | ||||
| 		smtutil::SortProvider::boolSort | ||||
| @ -835,7 +853,12 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) | ||||
| 
 | ||||
| 					m_summaries[contract].emplace(function, createSummaryBlock(*function, *contract)); | ||||
| 
 | ||||
| 					if (!base->isLibrary() && !base->isInterface() && !function->isConstructor()) | ||||
| 					if ( | ||||
| 						!function->isConstructor() && | ||||
| 						function->isPublic() && | ||||
| 						!base->isLibrary() && | ||||
| 						!base->isInterface() | ||||
| 					) | ||||
| 					{ | ||||
| 						auto state1 = stateVariablesAtIndex(1, *base); | ||||
| 						auto state2 = stateVariablesAtIndex(2, *base); | ||||
| @ -880,8 +903,13 @@ smtutil::Expression CHC::error(unsigned _idx) | ||||
| 	return m_errorPredicate->functionValueAtIndex(_idx)({}); | ||||
| } | ||||
| 
 | ||||
| smtutil::Expression CHC::summary(ContractDefinition const&) | ||||
| smtutil::Expression CHC::summary(ContractDefinition const& _contract) | ||||
| { | ||||
| 	if (auto const* constructor = _contract.constructor()) | ||||
| 		return (*m_constructorSummaryPredicate)( | ||||
| 			currentFunctionVariables(*constructor) | ||||
| 		); | ||||
| 
 | ||||
| 	return (*m_constructorSummaryPredicate)( | ||||
| 		vector<smtutil::Expression>{m_error.currentValue()} + | ||||
| 		currentStateVariables() | ||||
| @ -908,21 +936,28 @@ smtutil::Expression CHC::summary(FunctionDefinition const& _function) | ||||
| 
 | ||||
| unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix) | ||||
| { | ||||
| 	return createSymbolicBlock(sort(_node), | ||||
| 	auto block = createSymbolicBlock(sort(_node), | ||||
| 		"block_" + | ||||
| 		uniquePrefix() + | ||||
| 		"_" + | ||||
| 		_prefix + | ||||
| 		predicateName(_node)); | ||||
| 
 | ||||
| 	solAssert(m_currentFunction, ""); | ||||
| 	m_symbolFunction[block->currentFunctionValue().name] = m_currentFunction; | ||||
| 	return block; | ||||
| } | ||||
| 
 | ||||
| unique_ptr<smt::SymbolicFunctionVariable> CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) | ||||
| { | ||||
| 	return createSymbolicBlock(summarySort(_function, _contract), | ||||
| 	auto block = createSymbolicBlock(summarySort(_function, _contract), | ||||
| 		"summary_" + | ||||
| 		uniquePrefix() + | ||||
| 		"_" + | ||||
| 		predicateName(&_function, &_contract)); | ||||
| 
 | ||||
| 	m_symbolFunction[block->currentFunctionValue().name] = &_function; | ||||
| 	return block; | ||||
| } | ||||
| 
 | ||||
| void CHC::createErrorBlock() | ||||
| @ -977,15 +1012,21 @@ vector<smtutil::Expression> CHC::currentStateVariables(ContractDefinition const& | ||||
| } | ||||
| 
 | ||||
| vector<smtutil::Expression> CHC::currentFunctionVariables() | ||||
| { | ||||
| 	solAssert(m_currentFunction, ""); | ||||
| 	return currentFunctionVariables(*m_currentFunction); | ||||
| } | ||||
| 
 | ||||
| vector<smtutil::Expression> CHC::currentFunctionVariables(FunctionDefinition const& _function) | ||||
| { | ||||
| 	vector<smtutil::Expression> initInputExprs; | ||||
| 	vector<smtutil::Expression> mutableInputExprs; | ||||
| 	for (auto const& var: m_currentFunction->parameters()) | ||||
| 	for (auto const& var: _function.parameters()) | ||||
| 	{ | ||||
| 		initInputExprs.push_back(m_context.variable(*var)->valueAtIndex(0)); | ||||
| 		mutableInputExprs.push_back(m_context.variable(*var)->currentValue()); | ||||
| 	} | ||||
| 	auto returnExprs = applyMap(m_currentFunction->returnParameters(), [this](auto _var) { return currentValue(*_var); }); | ||||
| 	auto returnExprs = applyMap(_function.returnParameters(), [this](auto _var) { return currentValue(*_var); }); | ||||
| 	return vector<smtutil::Expression>{m_error.currentValue()} + | ||||
| 		initialStateVariables() + | ||||
| 		initInputExprs + | ||||
| @ -994,6 +1035,14 @@ vector<smtutil::Expression> CHC::currentFunctionVariables() | ||||
| 		returnExprs; | ||||
| } | ||||
| 
 | ||||
| vector<smtutil::Expression> CHC::currentFunctionVariables(ContractDefinition const& _contract) | ||||
| { | ||||
| 	if (auto const* constructor = _contract.constructor()) | ||||
| 		return currentFunctionVariables(*constructor); | ||||
| 
 | ||||
| 	return vector<smtutil::Expression>{m_error.currentValue()} + currentStateVariables(); | ||||
| } | ||||
| 
 | ||||
| vector<smtutil::Expression> CHC::currentBlockVariables() | ||||
| { | ||||
| 	if (m_currentFunction) | ||||
| @ -1074,15 +1123,33 @@ void CHC::addRule(smtutil::Expression const& _rule, string const& _ruleName) | ||||
| 	m_interface->addRule(_rule, _ruleName); | ||||
| } | ||||
| 
 | ||||
| pair<smtutil::CheckResult, vector<string>> CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location) | ||||
| pair<smtutil::CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location) | ||||
| { | ||||
| 	smtutil::CheckResult result; | ||||
| 	vector<string> values; | ||||
| 	tie(result, values) = m_interface->query(_query); | ||||
| 	CHCSolverInterface::CexGraph cex; | ||||
| 	tie(result, cex) = m_interface->query(_query); | ||||
| 	switch (result) | ||||
| 	{ | ||||
| 	case smtutil::CheckResult::SATISFIABLE: | ||||
| 	{ | ||||
| #ifdef HAVE_Z3 | ||||
| 		// Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete.
 | ||||
| 		// We now disable those optimizations and check whether we can still solve the problem.
 | ||||
| 		auto* spacer = dynamic_cast<Z3CHCInterface*>(m_interface.get()); | ||||
| 		solAssert(spacer, ""); | ||||
| 		spacer->setSpacerOptions(false); | ||||
| 
 | ||||
| 		smtutil::CheckResult resultNoOpt; | ||||
| 		CHCSolverInterface::CexGraph cexNoOpt; | ||||
| 		tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); | ||||
| 
 | ||||
| 		if (resultNoOpt == smtutil::CheckResult::SATISFIABLE) | ||||
| 			cex = move(cexNoOpt); | ||||
| 
 | ||||
| 		spacer->setSpacerOptions(true); | ||||
| #endif | ||||
| 		break; | ||||
| 	} | ||||
| 	case smtutil::CheckResult::UNSATISFIABLE: | ||||
| 		break; | ||||
| 	case smtutil::CheckResult::UNKNOWN: | ||||
| @ -1094,7 +1161,7 @@ pair<smtutil::CheckResult, vector<string>> CHC::query(smtutil::Expression const& | ||||
| 		m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver."); | ||||
| 		break; | ||||
| 	} | ||||
| 	return {result, values}; | ||||
| 	return {result, cex}; | ||||
| } | ||||
| 
 | ||||
| void CHC::addVerificationTarget( | ||||
| @ -1138,19 +1205,21 @@ void CHC::checkVerificationTargets() | ||||
| 		{ | ||||
| 			string satMsg; | ||||
| 			string unknownMsg; | ||||
| 			ErrorId errorReporterId; | ||||
| 
 | ||||
| 			if (target.type == VerificationTarget::Type::PopEmptyArray) | ||||
| 			{ | ||||
| 				solAssert(dynamic_cast<FunctionCall const*>(scope), ""); | ||||
| 				satMsg = "Empty array \"pop\" detected here."; | ||||
| 				satMsg = "Empty array \"pop\" detected here"; | ||||
| 				unknownMsg = "Empty array \"pop\" might happen here."; | ||||
| 				errorReporterId = 2529_error; | ||||
| 			} | ||||
| 			else | ||||
| 				solAssert(false, ""); | ||||
| 
 | ||||
| 			auto it = m_errorIds.find(scope->id()); | ||||
| 			solAssert(it != m_errorIds.end(), ""); | ||||
| 			checkAndReportTarget(scope, target, it->second, satMsg, unknownMsg); | ||||
| 			checkAndReportTarget(scope, target, it->second, errorReporterId, satMsg, unknownMsg); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @ -1165,13 +1234,7 @@ void CHC::checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& | ||||
| 		solAssert(it != m_errorIds.end(), ""); | ||||
| 		unsigned errorId = it->second; | ||||
| 
 | ||||
| 		createErrorBlock(); | ||||
| 		connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == errorId)); | ||||
| 		auto [result, model] = query(error(), assertion->location()); | ||||
| 		// This should be fine but it's a bug in the old compiler
 | ||||
| 		(void)model; | ||||
| 		if (result == smtutil::CheckResult::UNSATISFIABLE) | ||||
| 			m_safeTargets[assertion].insert(_target.type); | ||||
| 		checkAndReportTarget(assertion, _target, errorId, 6328_error, "Assertion violation happens here"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -1179,35 +1242,251 @@ void CHC::checkAndReportTarget( | ||||
| 	ASTNode const* _scope, | ||||
| 	CHCVerificationTarget const& _target, | ||||
| 	unsigned _errorId, | ||||
| 	ErrorId _errorReporterId, | ||||
| 	string _satMsg, | ||||
| 	string _unknownMsg | ||||
| ) | ||||
| { | ||||
| 	if (m_unsafeTargets.count(_scope) && m_unsafeTargets.at(_scope).count(_target.type)) | ||||
| 		return; | ||||
| 
 | ||||
| 	createErrorBlock(); | ||||
| 	connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId)); | ||||
| 	auto [result, model] = query(error(), _scope->location()); | ||||
| 	// This should be fine but it's a bug in the old compiler
 | ||||
| 	(void)model; | ||||
| 	auto const& [result, model] = query(error(), _scope->location()); | ||||
| 	if (result == smtutil::CheckResult::UNSATISFIABLE) | ||||
| 		m_safeTargets[_scope].insert(_target.type); | ||||
| 	else if (result == smtutil::CheckResult::SATISFIABLE) | ||||
| 	{ | ||||
| 		solAssert(!_satMsg.empty(), ""); | ||||
| 		m_unsafeTargets[_scope].insert(_target.type); | ||||
| 		m_outerErrorReporter.warning( | ||||
| 			2529_error, | ||||
| 			_scope->location(), | ||||
| 			_satMsg | ||||
| 		); | ||||
| 		auto cex = generateCounterexample(model, error().name); | ||||
| 		if (cex) | ||||
| 			m_outerErrorReporter.warning( | ||||
| 				_errorReporterId, | ||||
| 				_scope->location(), | ||||
| 				_satMsg, | ||||
| 				SecondarySourceLocation().append(" for:\n" + *cex, SourceLocation{}) | ||||
| 			); | ||||
| 		else | ||||
| 			m_outerErrorReporter.warning( | ||||
| 				_errorReporterId, | ||||
| 				_scope->location(), | ||||
| 				_satMsg + "." | ||||
| 			); | ||||
| 	} | ||||
| 	else if (!_unknownMsg.empty()) | ||||
| 		m_outerErrorReporter.warning( | ||||
| 			1147_error, | ||||
| 			_errorReporterId, | ||||
| 			_scope->location(), | ||||
| 			_unknownMsg | ||||
| 		); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| The counterexample DAG has the following properties: | ||||
| 1) The root node represents the reachable error predicate. | ||||
| 2) The root node has 1 or 2 children: | ||||
| 	- One of them is the summary of the function that was called and led to that node. | ||||
| 	If this is the only child, this function must be the constructor. | ||||
| 	- If it has 2 children, the function is not the constructor and the other child is the interface node, | ||||
| 	that is, it represents the state of the contract before the function described above was called. | ||||
| 3) Interface nodes also have property 2. | ||||
| 
 | ||||
| The following algorithm starts collecting function summaries at the root node and repeats | ||||
| for each interface node seen. | ||||
| Each function summary collected represents a transaction, and the final order is reversed. | ||||
| 
 | ||||
| The first function summary seen contains the values for the state, input and output variables at the | ||||
| error point. | ||||
| */ | ||||
| optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const& _graph, string const& _root) | ||||
| { | ||||
| 	optional<unsigned> rootId; | ||||
| 	for (auto const& [id, node]: _graph.nodes) | ||||
| 		if (node.first == _root) | ||||
| 		{ | ||||
| 			rootId = id; | ||||
| 			break; | ||||
| 		} | ||||
| 	if (!rootId) | ||||
| 		return {}; | ||||
| 
 | ||||
| 	vector<string> path; | ||||
| 	string localState; | ||||
| 
 | ||||
| 	unsigned node = *rootId; | ||||
| 	/// The first summary node seen in this loop represents the last transaction.
 | ||||
| 	bool lastTxSeen = false; | ||||
| 	while (_graph.edges.at(node).size() >= 1) | ||||
| 	{ | ||||
| 		auto const& edges = _graph.edges.at(node); | ||||
| 		solAssert(edges.size() <= 2, ""); | ||||
| 
 | ||||
| 		unsigned summaryId = edges.at(0); | ||||
| 
 | ||||
| 		optional<unsigned> interfaceId; | ||||
| 		if (edges.size() == 2) | ||||
| 		{ | ||||
| 			interfaceId = edges.at(1); | ||||
| 			if (_graph.nodes.at(summaryId).first.rfind("summary", 0) != 0) | ||||
| 				swap(summaryId, *interfaceId); | ||||
| 			solAssert(_graph.nodes.at(*interfaceId).first.rfind("interface", 0) == 0, ""); | ||||
| 		} | ||||
| 		/// The children are unordered, so we need to check which is the summary and
 | ||||
| 		/// which is the interface.
 | ||||
| 
 | ||||
| 		solAssert(_graph.nodes.at(summaryId).first.rfind("summary", 0) == 0, ""); | ||||
| 		/// At this point property 2 from the function description is verified for this node.
 | ||||
| 
 | ||||
| 		auto const& summaryNode = _graph.nodes.at(summaryId); | ||||
| 		solAssert(m_symbolFunction.count(summaryNode.first), ""); | ||||
| 
 | ||||
| 		FunctionDefinition const* calledFun = nullptr; | ||||
| 		ContractDefinition const* calledContract = nullptr; | ||||
| 		if (auto const* contract = dynamic_cast<ContractDefinition const*>(m_symbolFunction.at(summaryNode.first))) | ||||
| 		{ | ||||
| 			if (auto const* constructor = contract->constructor()) | ||||
| 				calledFun = constructor; | ||||
| 			else | ||||
| 				calledContract = contract; | ||||
| 		} | ||||
| 		else if (auto const* fun = dynamic_cast<FunctionDefinition const*>(m_symbolFunction.at(summaryNode.first))) | ||||
| 			calledFun = fun; | ||||
| 		else | ||||
| 			solAssert(false, ""); | ||||
| 
 | ||||
| 		solAssert((calledFun && !calledContract) || (!calledFun && calledContract), ""); | ||||
| 		auto const& stateVars = calledFun ? stateVariablesIncludingInheritedAndPrivate(*calledFun) : stateVariablesIncludingInheritedAndPrivate(*calledContract); | ||||
| 		/// calledContract != nullptr implies that the constructor of the analyzed contract is implicit and
 | ||||
| 		/// therefore takes no parameters.
 | ||||
| 
 | ||||
| 		/// This summary node is the end of a tx.
 | ||||
| 		/// If it is the first summary node seen in this loop, it is the summary
 | ||||
| 		/// of the public/external function that was called when the error was reached,
 | ||||
| 		/// but not necessarily the summary of the function that contains the error.
 | ||||
| 		if (!lastTxSeen) | ||||
| 		{ | ||||
| 			lastTxSeen = true; | ||||
| 			/// Generate counterexample message local to the failed target.
 | ||||
| 			localState = formatStateCounterexample(stateVars, calledFun, summaryNode.second) + "\n"; | ||||
| 			if (calledFun) | ||||
| 			{ | ||||
| 				/// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
 | ||||
| 				auto const& inParams = calledFun->parameters(); | ||||
| 				unsigned initLocals = stateVars.size() * 2 + 1 + inParams.size(); | ||||
| 				/// In this loop we are interested in postInputVars.
 | ||||
| 				for (unsigned i = initLocals; i < initLocals + inParams.size(); ++i) | ||||
| 				{ | ||||
| 					auto param = inParams.at(i - initLocals); | ||||
| 					if (param->type()->isValueType()) | ||||
| 						localState += param->name() + " = " + summaryNode.second.at(i) + "\n"; | ||||
| 				} | ||||
| 				auto const& outParams = calledFun->returnParameters(); | ||||
| 				initLocals += inParams.size(); | ||||
| 				/// In this loop we are interested in outputVars.
 | ||||
| 				for (unsigned i = initLocals; i < initLocals + outParams.size(); ++i) | ||||
| 				{ | ||||
| 					auto param = outParams.at(i - initLocals); | ||||
| 					if (param->type()->isValueType()) | ||||
| 						localState += param->name() + " = " + summaryNode.second.at(i) + "\n"; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 			/// We report the state after every tx in the trace except for the last, which is reported
 | ||||
| 			/// first in the code above.
 | ||||
| 			path.emplace_back("State: " + formatStateCounterexample(stateVars, calledFun, summaryNode.second)); | ||||
| 
 | ||||
| 		string txCex = calledContract ? "constructor()" : formatFunctionCallCounterexample(stateVars, *calledFun, summaryNode.second); | ||||
| 		path.emplace_back(txCex); | ||||
| 
 | ||||
| 		/// Recurse on the next interface node which represents the previous transaction
 | ||||
| 		/// or stop.
 | ||||
| 		if (interfaceId) | ||||
| 			node = *interfaceId; | ||||
| 		else | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); | ||||
| } | ||||
| 
 | ||||
| string CHC::formatStateCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const* _function, vector<string> const& _summaryValues) | ||||
| { | ||||
| 	/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
 | ||||
| 	/// The signature of an implicit constructor summary predicate is: summary(error, postStateVars).
 | ||||
| 	/// Here we are interested in postStateVars.
 | ||||
| 	vector<string>::const_iterator stateFirst; | ||||
| 	vector<string>::const_iterator stateLast; | ||||
| 	if (_function) | ||||
| 	{ | ||||
| 		stateFirst = _summaryValues.begin() + 1 + static_cast<int>(_stateVars.size()) + static_cast<int>(_function->parameters().size()); | ||||
| 		stateLast = stateFirst + static_cast<int>(_stateVars.size()); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		stateFirst = _summaryValues.begin() + 1; | ||||
| 		stateLast = stateFirst + static_cast<int>(_stateVars.size()); | ||||
| 	} | ||||
| 
 | ||||
| 	solAssert(stateFirst >= _summaryValues.begin() && stateFirst <= _summaryValues.end(), ""); | ||||
| 	solAssert(stateLast >= _summaryValues.begin() && stateLast <= _summaryValues.end(), ""); | ||||
| 	vector<string> stateArgs(stateFirst, stateLast); | ||||
| 	solAssert(stateArgs.size() == _stateVars.size(), ""); | ||||
| 
 | ||||
| 	vector<string> stateCex; | ||||
| 	for (unsigned i = 0; i < stateArgs.size(); ++i) | ||||
| 	{ | ||||
| 		auto var = _stateVars.at(i); | ||||
| 		if (var->type()->isValueType()) | ||||
| 			stateCex.emplace_back(var->name() + " = " + stateArgs.at(i)); | ||||
| 	} | ||||
| 
 | ||||
| 	return boost::algorithm::join(stateCex, ", "); | ||||
| } | ||||
| 
 | ||||
| string CHC::formatFunctionCallCounterexample(vector<VariableDeclaration const*> const& _stateVars, FunctionDefinition const& _function, vector<string> const& _summaryValues) | ||||
| { | ||||
| 	/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars).
 | ||||
| 	/// Here we are interested in preInputVars.
 | ||||
| 	vector<string>::const_iterator first = _summaryValues.begin() + static_cast<int>(_stateVars.size()) + 1; | ||||
| 	vector<string>::const_iterator last = first + static_cast<int>(_function.parameters().size()); | ||||
| 	solAssert(first >= _summaryValues.begin() && first <= _summaryValues.end(), ""); | ||||
| 	solAssert(last >= _summaryValues.begin() && last <= _summaryValues.end(), ""); | ||||
| 	vector<string> functionArgsCex(first, last); | ||||
| 	vector<string> functionArgs; | ||||
| 
 | ||||
| 	auto const& params = _function.parameters(); | ||||
| 	solAssert(params.size() == functionArgsCex.size(), ""); | ||||
| 	for (unsigned i = 0; i < params.size(); ++i) | ||||
| 		if (params[i]->type()->isValueType()) | ||||
| 			functionArgs.emplace_back(functionArgsCex[i]); | ||||
| 		else | ||||
| 			functionArgs.emplace_back(params[i]->name()); | ||||
| 
 | ||||
| 	string fName = _function.isConstructor() ? "constructor" : | ||||
| 		_function.isFallback() ? "fallback" : | ||||
| 		_function.isReceive() ? "receive" : | ||||
| 		_function.name(); | ||||
| 	return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")"; | ||||
| } | ||||
| 
 | ||||
| string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex) | ||||
| { | ||||
| 	string dot = "digraph {\n"; | ||||
| 
 | ||||
| 	auto pred = [&](CHCSolverInterface::CexNode const& _node) { | ||||
| 		return "\"" + _node.first + "(" + boost::algorithm::join(_node.second, ", ") + ")\""; | ||||
| 	}; | ||||
| 
 | ||||
| 	for (auto const& [u, vs]: _cex.edges) | ||||
| 		for (auto v: vs) | ||||
| 			dot += pred(_cex.nodes.at(v)) + " -> " + pred(_cex.nodes.at(u)) + "\n"; | ||||
| 
 | ||||
| 	dot += "}"; | ||||
| 	return dot; | ||||
| } | ||||
| 
 | ||||
| string CHC::uniquePrefix() | ||||
| { | ||||
| 	return to_string(m_blockCounter++); | ||||
|  | ||||
| @ -38,6 +38,7 @@ | ||||
| #include <libsmtutil/CHCSolverInterface.h> | ||||
| 
 | ||||
| #include <map> | ||||
| #include <optional> | ||||
| #include <set> | ||||
| 
 | ||||
| namespace solidity::frontend | ||||
| @ -102,6 +103,7 @@ private: | ||||
| 	void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector<smtutil::Expression> const* _arguments = nullptr); | ||||
| 	std::set<Expression const*, IdCompare> transactionAssertions(ASTNode const* _txRoot); | ||||
| 	static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); | ||||
| 	static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); | ||||
| 	//@}
 | ||||
| 
 | ||||
| 	/// Sort helpers.
 | ||||
| @ -164,6 +166,9 @@ private: | ||||
| 	/// @returns the current symbolic values of the current function's
 | ||||
| 	/// input and output parameters.
 | ||||
| 	std::vector<smtutil::Expression> currentFunctionVariables(); | ||||
| 	std::vector<smtutil::Expression> currentFunctionVariables(FunctionDefinition const& _function); | ||||
| 	std::vector<smtutil::Expression> currentFunctionVariables(ContractDefinition const& _contract); | ||||
| 
 | ||||
| 	/// @returns the same as currentFunctionVariables plus
 | ||||
| 	/// local variables.
 | ||||
| 	std::vector<smtutil::Expression> currentBlockVariables(); | ||||
| @ -189,7 +194,7 @@ private: | ||||
| 	void addRule(smtutil::Expression const& _rule, std::string const& _ruleName); | ||||
| 	/// @returns <true, empty> if query is unsatisfiable (safe).
 | ||||
| 	/// @returns <false, model> otherwise.
 | ||||
| 	std::pair<smtutil::CheckResult, std::vector<std::string>> query(smtutil::Expression const& _query, langutil::SourceLocation const& _location); | ||||
| 	std::pair<smtutil::CheckResult, smtutil::CHCSolverInterface::CexGraph> 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 addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); | ||||
| @ -203,9 +208,25 @@ private: | ||||
| 		ASTNode const* _scope, | ||||
| 		CHCVerificationTarget const& _target, | ||||
| 		unsigned _errorId, | ||||
| 		langutil::ErrorId _errorReporterId, | ||||
| 		std::string _satMsg, | ||||
| 		std::string _unknownMsg | ||||
| 		std::string _unknownMsg = "" | ||||
| 	); | ||||
| 
 | ||||
| 	std::optional<std::string> generateCounterexample(smtutil::CHCSolverInterface::CexGraph const& _graph, std::string const& _root); | ||||
| 	/// @returns values for the _stateVariables after a transaction calling
 | ||||
| 	/// _function was executed.
 | ||||
| 	/// _function = nullptr means the transaction was the deployment of a
 | ||||
| 	/// contract without an explicit constructor.
 | ||||
| 	std::string formatStateCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const* _function, std::vector<std::string> const& _summaryValues); | ||||
| 	/// @returns a formatted text representing a call to _function
 | ||||
| 	/// with the concrete values for value type parameters and
 | ||||
| 	/// the parameter name for reference types.
 | ||||
| 	std::string formatFunctionCallCounterexample(std::vector<VariableDeclaration const*> const& _stateVariables, FunctionDefinition const& _function, std::vector<std::string> const& _summaryValues); | ||||
| 
 | ||||
| 	/// @returns a DAG in the dot format.
 | ||||
| 	/// Used for debugging purposes.
 | ||||
| 	std::string cex2dot(smtutil::CHCSolverInterface::CexGraph const& _graph); | ||||
| 	//@}
 | ||||
| 
 | ||||
| 	/// Misc.
 | ||||
| @ -255,6 +276,9 @@ private: | ||||
| 		"error", | ||||
| 		m_context | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Maps predicate names to the ASTNodes they came from.
 | ||||
| 	std::map<std::string, ASTNode const*> m_symbolFunction; | ||||
| 	//@}
 | ||||
| 
 | ||||
| 	/// Variables.
 | ||||
|  | ||||
| @ -425,17 +425,13 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) | ||||
| 		auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_tuple)); | ||||
| 		solAssert(symbTuple, ""); | ||||
| 		auto const& symbComponents = symbTuple->components(); | ||||
| 		auto const* tupleComponents = &_tuple.components(); | ||||
| 		while (tupleComponents->size() == 1) | ||||
| 		{ | ||||
| 			auto innerTuple = dynamic_pointer_cast<TupleExpression>(tupleComponents->front()); | ||||
| 			solAssert(innerTuple, ""); | ||||
| 			tupleComponents = &innerTuple->components(); | ||||
| 		} | ||||
| 		solAssert(symbComponents.size() == tupleComponents->size(), ""); | ||||
| 		auto const* tuple = dynamic_cast<TupleExpression const*>(innermostTuple(_tuple)); | ||||
| 		solAssert(tuple, ""); | ||||
| 		auto const& tupleComponents = tuple->components(); | ||||
| 		solAssert(symbComponents.size() == tupleComponents.size(), ""); | ||||
| 		for (unsigned i = 0; i < symbComponents.size(); ++i) | ||||
| 		{ | ||||
| 			auto tComponent = tupleComponents->at(i); | ||||
| 			auto tComponent = tupleComponents.at(i); | ||||
| 			if (tComponent) | ||||
| 			{ | ||||
| 				if (auto varDecl = identifierToVariable(*tComponent)) | ||||
| @ -454,8 +450,7 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) | ||||
| 		/// Parenthesized expressions are also TupleExpression regardless their type.
 | ||||
| 		auto const& components = _tuple.components(); | ||||
| 		solAssert(components.size() == 1, ""); | ||||
| 		if (smt::isSupportedType(components.front()->annotation().type->category())) | ||||
| 			defineExpr(_tuple, expr(*components.front())); | ||||
| 		defineExpr(_tuple, expr(*components.front())); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -729,11 +724,7 @@ void SMTEncoder::visitGasLeft(FunctionCall const& _funCall) | ||||
| 
 | ||||
| void SMTEncoder::endVisit(Identifier const& _identifier) | ||||
| { | ||||
| 	if (_identifier.annotation().willBeWrittenTo) | ||||
| 	{ | ||||
| 		// Will be translated as part of the node that requested the lvalue.
 | ||||
| 	} | ||||
| 	else if (auto decl = identifierToVariable(_identifier)) | ||||
| 	if (auto decl = identifierToVariable(_identifier)) | ||||
| 		defineExpr(_identifier, currentValue(*decl)); | ||||
| 	else if (_identifier.annotation().type->category() == Type::Category::Function) | ||||
| 		visitFunctionIdentifier(_identifier); | ||||
| @ -1109,7 +1100,11 @@ void SMTEncoder::arrayPop(FunctionCall const& _funCall) | ||||
| 
 | ||||
| void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression const& _array) | ||||
| { | ||||
| 	if (auto const* id = dynamic_cast<Identifier const*>(&_expr)) | ||||
| 	Expression const* expr = &_expr; | ||||
| 	if (auto const* tupleExpr = dynamic_cast<TupleExpression const*>(expr)) | ||||
| 		expr = innermostTuple(*tupleExpr); | ||||
| 
 | ||||
| 	if (auto const* id = dynamic_cast<Identifier const*>(expr)) | ||||
| 	{ | ||||
| 		auto varDecl = identifierToVariable(*id); | ||||
| 		solAssert(varDecl, ""); | ||||
| @ -1117,9 +1112,9 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression | ||||
| 			resetReferences(*varDecl); | ||||
| 		m_context.addAssertion(m_context.newValue(*varDecl) == _array); | ||||
| 	} | ||||
| 	else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(&_expr)) | ||||
| 	else if (auto const* indexAccess = dynamic_cast<IndexAccess const*>(expr)) | ||||
| 		arrayIndexAssignment(*indexAccess, _array); | ||||
| 	else if (auto const* funCall = dynamic_cast<FunctionCall const*>(&_expr)) | ||||
| 	else if (auto const* funCall = dynamic_cast<FunctionCall const*>(expr)) | ||||
| 	{ | ||||
| 		FunctionType const& funType = dynamic_cast<FunctionType const&>(*funCall->expression().annotation().type); | ||||
| 		if (funType.kind() == FunctionType::Kind::ArrayPush) | ||||
| @ -1141,7 +1136,7 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression | ||||
| 			arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue()); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (dynamic_cast<MemberAccess const*>(&_expr)) | ||||
| 	else if (dynamic_cast<MemberAccess const*>(expr)) | ||||
| 		m_errorReporter.warning( | ||||
| 			9599_error, | ||||
| 			_expr.location(), | ||||
| @ -1449,10 +1444,14 @@ void SMTEncoder::assignment( | ||||
| 		"Tuple assignments should be handled by tupleAssignment." | ||||
| 	); | ||||
| 
 | ||||
| 	Expression const* left = &_left; | ||||
| 	if (auto const* tuple = dynamic_cast<TupleExpression const*>(left)) | ||||
| 		left = innermostTuple(*tuple); | ||||
| 
 | ||||
| 	if (!smt::isSupportedType(_type->category())) | ||||
| 	{ | ||||
| 		// Give it a new index anyway to keep the SSA scheme sound.
 | ||||
| 		if (auto varDecl = identifierToVariable(_left)) | ||||
| 		if (auto varDecl = identifierToVariable(*left)) | ||||
| 			m_context.newValue(*varDecl); | ||||
| 
 | ||||
| 		m_errorReporter.warning( | ||||
| @ -1461,10 +1460,10 @@ void SMTEncoder::assignment( | ||||
| 			"Assertion checker does not yet implement type " + _type->toString() | ||||
| 		); | ||||
| 	} | ||||
| 	else if (auto varDecl = identifierToVariable(_left)) | ||||
| 	else if (auto varDecl = identifierToVariable(*left)) | ||||
| 		assignment(*varDecl, _right); | ||||
| 	else if (dynamic_cast<IndexAccess const*>(&_left)) | ||||
| 		arrayIndexAssignment(_left, _right); | ||||
| 	else if (dynamic_cast<IndexAccess const*>(left)) | ||||
| 		arrayIndexAssignment(*left, _right); | ||||
| 	else | ||||
| 		m_errorReporter.warning( | ||||
| 			8182_error, | ||||
| @ -1475,7 +1474,7 @@ void SMTEncoder::assignment( | ||||
| 
 | ||||
| void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right) | ||||
| { | ||||
| 	auto lTuple = dynamic_cast<TupleExpression const*>(&_left); | ||||
| 	auto lTuple = dynamic_cast<TupleExpression const*>(innermostTuple(dynamic_cast<TupleExpression const&>(_left))); | ||||
| 	solAssert(lTuple, ""); | ||||
| 
 | ||||
| 	auto const& lComponents = lTuple->components(); | ||||
| @ -1901,6 +1900,20 @@ Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) | ||||
| 	return base; | ||||
| } | ||||
| 
 | ||||
| Expression const* SMTEncoder::innermostTuple(TupleExpression const& _tuple) | ||||
| { | ||||
| 	solAssert(!_tuple.isInlineArray(), ""); | ||||
| 	TupleExpression const* tuple = &_tuple; | ||||
| 	Expression const* expr = tuple; | ||||
| 	while (tuple && !tuple->isInlineArray() && tuple->components().size() == 1) | ||||
| 	{ | ||||
| 		expr = tuple->components().front().get(); | ||||
| 		tuple = dynamic_cast<TupleExpression const*>(expr); | ||||
| 	} | ||||
| 	solAssert(expr, ""); | ||||
| 	return expr; | ||||
| } | ||||
| 
 | ||||
| set<VariableDeclaration const*> SMTEncoder::touchedVariables(ASTNode const& _node) | ||||
| { | ||||
| 	vector<CallableDeclaration const*> callStack; | ||||
|  | ||||
| @ -54,6 +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 FunctionDefinition of a FunctionCall
 | ||||
| 	/// if possible or nullptr.
 | ||||
| 	static FunctionDefinition const* functionCallToDefinition(FunctionCall const& _funCall); | ||||
|  | ||||
| @ -75,7 +75,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) | ||||
| 		abi.emplace(std::move(method)); | ||||
| 	} | ||||
| 	FunctionDefinition const* constructor = _contractDef.constructor(); | ||||
| 	if (constructor && constructor->visibility() >= Visibility::Public) | ||||
| 	if (constructor && !_contractDef.abstract()) | ||||
| 	{ | ||||
| 		FunctionType constrType(*constructor); | ||||
| 		FunctionType const* externalFunctionType = constrType.interfaceFunctionType(); | ||||
|  | ||||
| @ -516,7 +516,17 @@ bool CompilerStack::compile() | ||||
| 			if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) | ||||
| 				if (isRequestedContract(*contract)) | ||||
| 				{ | ||||
| 					compileContract(*contract, otherCompilers); | ||||
| 					try | ||||
| 					{ | ||||
| 						compileContract(*contract, otherCompilers); | ||||
| 					} | ||||
| 					catch (Error const& _error) | ||||
| 					{ | ||||
| 						if (_error.type() != Error::Type::CodeGenerationError) | ||||
| 							throw; | ||||
| 						m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); | ||||
| 						return false; | ||||
| 					} | ||||
| 					if (m_generateIR || m_generateEwasm) | ||||
| 						generateIR(*contract); | ||||
| 					if (m_generateEwasm) | ||||
|  | ||||
| @ -47,8 +47,12 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) | ||||
| 	{ | ||||
| 		string const value = extractDoc(constructorDefinition->annotation().docTags, "notice"); | ||||
| 		if (!value.empty()) | ||||
| 		{ | ||||
| 			// add the constructor, only if we have any documentation to add
 | ||||
| 			methods["constructor"] = Json::Value(value); | ||||
| 			Json::Value user; | ||||
| 			user["notice"] = Json::Value(value); | ||||
| 			methods["constructor"] = user; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	string notice = extractDoc(_contractDef.annotation().docTags, "notice"); | ||||
|  | ||||
| @ -465,14 +465,6 @@ StateMutability Parser::parseStateMutability() | ||||
| 		case Token::Pure: | ||||
| 			stateMutability = StateMutability::Pure; | ||||
| 			break; | ||||
| 		case Token::Constant: | ||||
| 			stateMutability = StateMutability::View; | ||||
| 			parserError( | ||||
| 				7698_error, | ||||
| 				"The state mutability modifier \"constant\" was removed in version 0.5.0. " | ||||
| 				"Use \"view\" or \"pure\" instead." | ||||
| 			); | ||||
| 			break; | ||||
| 		default: | ||||
| 			solAssert(false, "Invalid state mutability specifier."); | ||||
| 	} | ||||
| @ -686,19 +678,13 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	ASTNodeFactory nodeFactory = _lookAheadArrayType ? | ||||
| 		ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); | ||||
| 	ASTPointer<TypeName> type; | ||||
| 
 | ||||
| 	ASTPointer<StructuredDocumentation> const documentation = parseStructuredDocumentation(); | ||||
| 	if (_lookAheadArrayType) | ||||
| 		type = _lookAheadArrayType; | ||||
| 	else | ||||
| 	{ | ||||
| 		type = parseTypeName(_options.allowVar); | ||||
| 		if (type != nullptr) | ||||
| 			nodeFactory.setEndPositionFromNode(type); | ||||
| 	} | ||||
| 	ASTPointer<TypeName> type = _lookAheadArrayType ? _lookAheadArrayType : parseTypeName(); | ||||
| 	nodeFactory.setEndPositionFromNode(type); | ||||
| 
 | ||||
| 	if (!_options.isStateVariable && documentation != nullptr) | ||||
| 		parserWarning(2837_error, "Only state variables can have a docstring. This will be disallowed in 0.7.0."); | ||||
| 		parserError(2837_error, "Only state variables can have a docstring."); | ||||
| 
 | ||||
| 	if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace) | ||||
| 		fatalParserError( | ||||
| @ -762,8 +748,6 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( | ||||
| 			{ | ||||
| 				if (location != VariableDeclaration::Location::Unspecified) | ||||
| 					parserError(3548_error, "Location already specified."); | ||||
| 				else if (!type) | ||||
| 					parserError(7439_error, "Location specifier needs explicit type name."); | ||||
| 				else | ||||
| 				{ | ||||
| 					switch (token) | ||||
| @ -790,10 +774,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( | ||||
| 	} | ||||
| 
 | ||||
| 	if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier) | ||||
| 	{ | ||||
| 		identifier = make_shared<ASTString>(""); | ||||
| 		solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense
 | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		nodeFactory.markEndPosition(); | ||||
| @ -917,7 +898,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective() | ||||
| 	if (m_scanner->currentToken() == Token::Mul) | ||||
| 		m_scanner->next(); | ||||
| 	else | ||||
| 		typeName = parseTypeName(false); | ||||
| 		typeName = parseTypeName(); | ||||
| 	nodeFactory.markEndPosition(); | ||||
| 	expectToken(Token::Semicolon); | ||||
| 	return nodeFactory.createNode<UsingForDirective>(library, typeName); | ||||
| @ -980,7 +961,7 @@ ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTN | ||||
| 	return type; | ||||
| } | ||||
| 
 | ||||
| ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) | ||||
| ASTPointer<TypeName> Parser::parseTypeName() | ||||
| { | ||||
| 	RecursionGuard recursionGuard(*this); | ||||
| 	ASTNodeFactory nodeFactory(*this); | ||||
| @ -998,7 +979,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) | ||||
| 		auto stateMutability = elemTypeName.token() == Token::Address | ||||
| 			? optional<StateMutability>{StateMutability::NonPayable} | ||||
| 			: nullopt; | ||||
| 		if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) | ||||
| 		if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken())) | ||||
| 		{ | ||||
| 			if (elemTypeName.token() == Token::Address) | ||||
| 			{ | ||||
| @ -1013,12 +994,6 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) | ||||
| 		} | ||||
| 		type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability); | ||||
| 	} | ||||
| 	else if (token == Token::Var) | ||||
| 	{ | ||||
| 		if (!_allowVar) | ||||
| 			parserError(7059_error, "Expected explicit type name."); | ||||
| 		m_scanner->next(); | ||||
| 	} | ||||
| 	else if (token == Token::Function) | ||||
| 		type = parseFunctionType(); | ||||
| 	else if (token == Token::Mapping) | ||||
| @ -1028,9 +1003,10 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) | ||||
| 	else | ||||
| 		fatalParserError(3546_error, "Expected type name"); | ||||
| 
 | ||||
| 	if (type) | ||||
| 		// Parse "[...]" postfixes for arrays.
 | ||||
| 		type = parseTypeNameSuffix(type, nodeFactory); | ||||
| 	solAssert(type, ""); | ||||
| 	// Parse "[...]" postfixes for arrays.
 | ||||
| 	type = parseTypeNameSuffix(type, nodeFactory); | ||||
| 
 | ||||
| 	return type; | ||||
| } | ||||
| 
 | ||||
| @ -1071,8 +1047,7 @@ ASTPointer<Mapping> Parser::parseMapping() | ||||
| 	else | ||||
| 		fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type"); | ||||
| 	expectToken(Token::Arrow); | ||||
| 	bool const allowVar = false; | ||||
| 	ASTPointer<TypeName> valueType = parseTypeName(allowVar); | ||||
| 	ASTPointer<TypeName> valueType = parseTypeName(); | ||||
| 	nodeFactory.markEndPosition(); | ||||
| 	expectToken(Token::RParen); | ||||
| 	return nodeFactory.createNode<Mapping>(keyType, valueType); | ||||
| @ -1553,53 +1528,14 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme | ||||
| 	ASTNodeFactory nodeFactory(*this); | ||||
| 	if (_lookAheadArrayType) | ||||
| 		nodeFactory.setLocation(_lookAheadArrayType->location()); | ||||
| 
 | ||||
| 	VarDeclParserOptions options; | ||||
| 	options.allowLocationSpecifier = true; | ||||
| 	vector<ASTPointer<VariableDeclaration>> variables; | ||||
| 	variables.emplace_back(parseVariableDeclaration(options, _lookAheadArrayType)); | ||||
| 	nodeFactory.setEndPositionFromNode(variables.back()); | ||||
| 
 | ||||
| 	ASTPointer<Expression> value; | ||||
| 	if ( | ||||
| 		!_lookAheadArrayType && | ||||
| 		m_scanner->currentToken() == Token::Var && | ||||
| 		m_scanner->peekNextToken() == Token::LParen | ||||
| 	) | ||||
| 	{ | ||||
| 		// Parse `var (a, b, ,, c) = ...` into a single VariableDeclarationStatement with multiple variables.
 | ||||
| 		m_scanner->next(); | ||||
| 		m_scanner->next(); | ||||
| 		if (m_scanner->currentToken() != Token::RParen) | ||||
| 			while (true) | ||||
| 			{ | ||||
| 				ASTPointer<VariableDeclaration> var; | ||||
| 				if ( | ||||
| 					m_scanner->currentToken() != Token::Comma && | ||||
| 					m_scanner->currentToken() != Token::RParen | ||||
| 				) | ||||
| 				{ | ||||
| 					ASTNodeFactory varDeclNodeFactory(*this); | ||||
| 					varDeclNodeFactory.markEndPosition(); | ||||
| 					ASTPointer<ASTString> name = expectIdentifierToken(); | ||||
| 					var = varDeclNodeFactory.createNode<VariableDeclaration>( | ||||
| 						ASTPointer<TypeName>(), | ||||
| 						name, | ||||
| 						ASTPointer<Expression>(), | ||||
| 						Visibility::Default | ||||
| 					); | ||||
| 				} | ||||
| 				variables.push_back(var); | ||||
| 				if (m_scanner->currentToken() == Token::RParen) | ||||
| 					break; | ||||
| 				else | ||||
| 					expectToken(Token::Comma); | ||||
| 			} | ||||
| 		nodeFactory.markEndPosition(); | ||||
| 		m_scanner->next(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		VarDeclParserOptions options; | ||||
| 		options.allowVar = true; | ||||
| 		options.allowLocationSpecifier = true; | ||||
| 		variables.push_back(parseVariableDeclaration(options, _lookAheadArrayType)); | ||||
| 		nodeFactory.setEndPositionFromNode(variables.back()); | ||||
| 	} | ||||
| 	if (m_scanner->currentToken() == Token::Assign) | ||||
| 	{ | ||||
| 		m_scanner->next(); | ||||
| @ -1713,11 +1649,8 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression( | ||||
| 	else if (m_scanner->currentToken() == Token::New) | ||||
| 	{ | ||||
| 		expectToken(Token::New); | ||||
| 		ASTPointer<TypeName> typeName(parseTypeName(false)); | ||||
| 		if (typeName) | ||||
| 			nodeFactory.setEndPositionFromNode(typeName); | ||||
| 		else | ||||
| 			nodeFactory.markEndPosition(); | ||||
| 		ASTPointer<TypeName> typeName(parseTypeName()); | ||||
| 		nodeFactory.setEndPositionFromNode(typeName); | ||||
| 		expression = nodeFactory.createNode<NewExpression>(typeName); | ||||
| 	} | ||||
| 	else if (m_scanner->currentToken() == Token::Payable) | ||||
| @ -1826,16 +1759,11 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() | ||||
| 		expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); | ||||
| 		break; | ||||
| 	case Token::Number: | ||||
| 		if ( | ||||
| 			(m_scanner->peekNextToken() == Token::Identifier && m_scanner->peekLiteral() == "gwei") || | ||||
| 			TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()) | ||||
| 		) | ||||
| 		if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())) | ||||
| 		{ | ||||
| 			ASTPointer<ASTString> literal = getLiteralAndAdvance(); | ||||
| 			nodeFactory.markEndPosition(); | ||||
| 			Token actualToken = m_scanner->currentToken() == Token::Identifier ? Token::SubGwei : m_scanner->currentToken(); | ||||
| 
 | ||||
| 			Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(actualToken); | ||||
| 			Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken()); | ||||
| 			m_scanner->next(); | ||||
| 			expression = nodeFactory.createNode<Literal>(token, literal, subdenomination); | ||||
| 		} | ||||
| @ -1854,6 +1782,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() | ||||
| 		} | ||||
| 		break; | ||||
| 	case Token::StringLiteral: | ||||
| 	case Token::UnicodeStringLiteral: | ||||
| 	case Token::HexStringLiteral: | ||||
| 	{ | ||||
| 		string literal = m_scanner->currentLiteral(); | ||||
| @ -2062,7 +1991,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const | ||||
| 	Token token(m_scanner->currentToken()); | ||||
| 	bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier); | ||||
| 
 | ||||
| 	if (token == Token::Mapping || token == Token::Function || token == Token::Var) | ||||
| 	if (token == Token::Mapping || token == Token::Function) | ||||
| 		return LookAheadInfo::VariableDeclaration; | ||||
| 	if (mightBeTypeName) | ||||
| 	{ | ||||
| @ -2071,7 +2000,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const | ||||
| 		// kind of statement. This means, for example, that we do not allow type expressions of the form
 | ||||
| 		// ``address payable;``.
 | ||||
| 		// If we want to change this in the future, we need to consider another scanner token here.
 | ||||
| 		if (TokenTraits::isElementaryTypeName(token) && TokenTraits::isStateMutabilitySpecifier(next, false)) | ||||
| 		if (TokenTraits::isElementaryTypeName(token) && TokenTraits::isStateMutabilitySpecifier(next)) | ||||
| 			return LookAheadInfo::VariableDeclaration; | ||||
| 		if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next)) | ||||
| 			return LookAheadInfo::VariableDeclaration; | ||||
|  | ||||
| @ -58,7 +58,6 @@ private: | ||||
| 		// https://stackoverflow.com/questions/17430377
 | ||||
| 		VarDeclParserOptions() {} | ||||
| 
 | ||||
| 		bool allowVar = false; | ||||
| 		bool isStateVariable = false; | ||||
| 		bool allowIndexed = false; | ||||
| 		bool allowEmptyName = false; | ||||
| @ -108,7 +107,7 @@ private: | ||||
| 	ASTPointer<Identifier> parseIdentifier(); | ||||
| 	ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName(); | ||||
| 	ASTPointer<TypeName> parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory); | ||||
| 	ASTPointer<TypeName> parseTypeName(bool _allowVar); | ||||
| 	ASTPointer<TypeName> parseTypeName(); | ||||
| 	ASTPointer<FunctionTypeName> parseFunctionType(); | ||||
| 	ASTPointer<Mapping> parseMapping(); | ||||
| 	ASTPointer<ParameterList> parseParameterList( | ||||
|  | ||||
| @ -88,8 +88,31 @@ bool parse(Json::CharReaderBuilder& _builder, string const& _input, Json::Value& | ||||
| 	return reader->parse(_input.c_str(), _input.c_str() + _input.length(), &_json, _errs); | ||||
| } | ||||
| 
 | ||||
| /// Takes a JSON value (@ _json) and removes all its members with value 'null' recursively.
 | ||||
| void removeNullMembersHelper(Json::Value& _json) | ||||
| { | ||||
| 	if (_json.type() == Json::ValueType::arrayValue) | ||||
| 		for (auto& child: _json) | ||||
| 			removeNullMembersHelper(child); | ||||
| 	else if (_json.type() == Json::ValueType::objectValue) | ||||
| 		for (auto const& key: _json.getMemberNames()) | ||||
| 		{ | ||||
| 			Json::Value& value = _json[key]; | ||||
| 			if (value.isNull()) | ||||
| 				_json.removeMember(key); | ||||
| 			else | ||||
| 				removeNullMembersHelper(value); | ||||
| 		} | ||||
| } | ||||
| 
 | ||||
| } // end anonymous namespace
 | ||||
| 
 | ||||
| Json::Value removeNullMembers(Json::Value _json) | ||||
| { | ||||
| 	removeNullMembersHelper(_json); | ||||
| 	return _json; | ||||
| } | ||||
| 
 | ||||
| string jsonPrettyPrint(Json::Value const& _input) | ||||
| { | ||||
| 	static map<string, Json::Value> settings{{"indentation", "  "}, {"enableYAMLCompatibility", true}}; | ||||
|  | ||||
| @ -29,6 +29,9 @@ | ||||
| 
 | ||||
| namespace solidity::util { | ||||
| 
 | ||||
| /// Removes members with null value recursively from (@a _json).
 | ||||
| Json::Value removeNullMembers(Json::Value _json); | ||||
| 
 | ||||
| /// Serialise the JSON object (@a _input) with indentation
 | ||||
| std::string jsonPrettyPrint(Json::Value const& _input); | ||||
| 
 | ||||
|  | ||||
| @ -209,7 +209,10 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) | ||||
| 				m_currentScope->insideFunction() | ||||
| 			); | ||||
| 	for (auto const& variable: _varDecl.variables) | ||||
| 	{ | ||||
| 		expectValidIdentifier(variable.name, variable.location); | ||||
| 		expectValidType(variable.type, variable.location); | ||||
| 	} | ||||
| 
 | ||||
| 	if (_varDecl.value) | ||||
| 	{ | ||||
| @ -249,11 +252,13 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) | ||||
| void AsmAnalyzer::operator()(FunctionDefinition const& _funDef) | ||||
| { | ||||
| 	yulAssert(!_funDef.name.empty(), ""); | ||||
| 	expectValidIdentifier(_funDef.name, _funDef.location); | ||||
| 	Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); | ||||
| 	yulAssert(virtualBlock, ""); | ||||
| 	Scope& varScope = scope(virtualBlock); | ||||
| 	for (auto const& var: _funDef.parameters + _funDef.returnVariables) | ||||
| 	{ | ||||
| 		expectValidIdentifier(var.name, var.location); | ||||
| 		expectValidType(var.type, var.location); | ||||
| 		m_activeVariables.insert(&std::get<Scope::Variable>(varScope.identifiers.at(var.name))); | ||||
| 	} | ||||
| @ -529,6 +534,26 @@ Scope& AsmAnalyzer::scope(Block const* _block) | ||||
| 	return *scopePtr; | ||||
| } | ||||
| 
 | ||||
| void AsmAnalyzer::expectValidIdentifier(YulString _identifier, SourceLocation const& _location) | ||||
| { | ||||
| 	// NOTE: the leading dot case is handled by the parser not allowing it.
 | ||||
| 
 | ||||
| 	if (boost::ends_with(_identifier.str(), ".")) | ||||
| 		m_errorReporter.syntaxError( | ||||
| 			3384_error, | ||||
| 			_location, | ||||
| 			"\"" + _identifier.str() + "\" is not a valid identifier (ends with a dot)." | ||||
| 		); | ||||
| 
 | ||||
| 	if (_identifier.str().find("..") != std::string::npos) | ||||
| 		m_errorReporter.syntaxError( | ||||
| 			7771_error, | ||||
| 			_location, | ||||
| 			"\"" + _identifier.str() + "\" is not a valid identifier (contains consecutive dots)." | ||||
| 		); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location) | ||||
| { | ||||
| 	if (!m_dialect.types.count(_type)) | ||||
| @ -611,16 +636,16 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio | ||||
| 		errorForVM(7110_error, "only available for Constantinople-compatible"); | ||||
| 	else if (_instr == evmasm::Instruction::CHAINID && !m_evmVersion.hasChainID()) | ||||
| 		errorForVM(1561_error, "only available for Istanbul-compatible"); | ||||
| 	else if (_instr == evmasm::Instruction::PC) | ||||
| 		m_errorReporter.warning( | ||||
| 			2450_error, | ||||
| 			_location, | ||||
| 			"The \"" + | ||||
| 			boost::to_lower_copy(instructionInfo(_instr).name) + | ||||
| 			"\" instruction is deprecated and will be removed in the next breaking release." | ||||
| 		); | ||||
| 	else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance()) | ||||
| 		errorForVM(3672_error, "only available for Istanbul-compatible"); | ||||
| 		errorForVM(7721_error, "only available for Istanbul-compatible"); | ||||
| 	else if (_instr == evmasm::Instruction::PC) | ||||
| 		m_errorReporter.error( | ||||
| 			2450_error, | ||||
| 			Error::Type::SyntaxError, | ||||
| 			_location, | ||||
| 			"PC instruction is a low-level EVM feature. " | ||||
| 			"Because of that PC is disallowed in strict assembly." | ||||
| 		); | ||||
| 	else | ||||
| 		return false; | ||||
| 
 | ||||
|  | ||||
| @ -108,6 +108,7 @@ private: | ||||
| 	void checkAssignment(Identifier const& _variable, YulString _valueType); | ||||
| 
 | ||||
| 	Scope& scope(Block const* _block); | ||||
| 	void expectValidIdentifier(YulString _identifier, langutil::SourceLocation const& _location); | ||||
| 	void expectValidType(YulString _type, langutil::SourceLocation const& _location); | ||||
| 	void expectType(YulString _expectedType, YulString _givenType, langutil::SourceLocation const& _location); | ||||
| 
 | ||||
|  | ||||
| @ -163,14 +163,21 @@ NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): | ||||
| { | ||||
| 	for (auto& fun: m_functions) | ||||
| 	{ | ||||
| 		size_t parameters = fun.second.parameters.size(); | ||||
| 		size_t returns = fun.second.returns.size(); | ||||
| 		fun.second.generateCode = [=](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&, std::function<void(Expression const&)> _visitExpression) | ||||
| 		{ | ||||
| 			for (auto const& arg: _call.arguments | boost::adaptors::reversed) | ||||
| 				_visitExpression(arg); | ||||
| 			size_t visited = 0; | ||||
| 			for (size_t j = 0; j < _call.arguments.size(); j++) | ||||
| 			{ | ||||
| 				size_t const i = _call.arguments.size() - j - 1; | ||||
| 				if (!(fun.second.literalArguments && (*fun.second.literalArguments)[i])) | ||||
| 				{ | ||||
| 					_visitExpression(_call.arguments[i]); | ||||
| 					visited++; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			for (size_t i = 0; i < parameters; i++) | ||||
| 			for (size_t i = 0; i < visited; i++) | ||||
| 				_assembly.appendInstruction(evmasm::Instruction::POP); | ||||
| 
 | ||||
| 			for (size_t i = 0; i < returns; i++) | ||||
|  | ||||
| @ -1024,11 +1024,6 @@ function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { | ||||
| 	eth.storageStore(0:i32, 32:i32) | ||||
| } | ||||
| 
 | ||||
| // Needed?
 | ||||
| function pc() -> z1, z2, z3, z4 { | ||||
| 	// TODO implement
 | ||||
| 	unreachable() | ||||
| } | ||||
| function gas() -> z1, z2, z3, z4 { | ||||
| 	z4 := eth.getGasLeft() | ||||
| } | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
| # (c) 2016-2019 solidity contributors. | ||||
| #------------------------------------------------------------------------------ | ||||
| FROM gcr.io/oss-fuzz-base/base-clang as base | ||||
| LABEL version="1" | ||||
| LABEL version="2" | ||||
| 
 | ||||
| ARG DEBIAN_FRONTEND=noninteractive | ||||
| 
 | ||||
| @ -96,6 +96,18 @@ RUN set -ex; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/evmone | ||||
| 
 | ||||
| # HERA | ||||
| RUN set -ex; \ | ||||
| 	cd /usr/src; \ | ||||
| 	git clone --branch="v0.3.0" --recurse-submodules https://github.com/ewasm/hera.git; \ | ||||
| 	cd hera; \ | ||||
| 	mkdir build; \ | ||||
| 	cd build; \ | ||||
| 	cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \ | ||||
| 	ninja; \ | ||||
| 	ninja install/strip; \ | ||||
| 	rm -rf /usr/src/hera | ||||
| 
 | ||||
| FROM base | ||||
| COPY --from=libraries /usr/lib /usr/lib | ||||
| COPY --from=libraries /usr/bin /usr/bin | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
| # (c) 2016-2019 solidity contributors. | ||||
| #------------------------------------------------------------------------------ | ||||
| FROM buildpack-deps:bionic AS base | ||||
| LABEL version="1" | ||||
| LABEL version="2" | ||||
| 
 | ||||
| ARG DEBIAN_FRONTEND=noninteractive | ||||
| 
 | ||||
| @ -88,6 +88,19 @@ RUN set -ex; \ | ||||
| 	tar xzpf $TGZFILE -C /usr; \ | ||||
| 	rm -f $TGZFILE; | ||||
| 
 | ||||
| # HERA | ||||
| ARG HERA_HASH="622663cb3bc1fa07213c6e1fe8070c5c259096e75039a46d014b2a3448b5cf44" | ||||
| ARG HERA_MAJOR="0" | ||||
| ARG HERA_MINOR="3" | ||||
| ARG HERA_MICRO="0" | ||||
| RUN set -ex; \ | ||||
| 	HERA_VERSION="$HERA_MAJOR.$HERA_MINOR.$HERA_MICRO"; \ | ||||
| 	TGZFILE="hera-$HERA_VERSION-linux-x86_64.tar.gz"; \ | ||||
| 	wget https://github.com/ewasm/hera/releases/download/v$HERA_VERSION/$TGZFILE; \ | ||||
| 	sha256sum $TGZFILE; \ | ||||
| 	tar xzpf $TGZFILE -C /usr; \ | ||||
| 	rm -f $TGZFILE; | ||||
| 
 | ||||
| FROM base | ||||
| COPY --from=libraries /usr/lib /usr/lib | ||||
| COPY --from=libraries /usr/bin /usr/bin | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user