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); }
|
||||
|
||||
@ -730,4 +730,4 @@ in scope in the block that follows.
|
||||
out-of-gas situation and not a deliberate error condition:
|
||||
The caller always retains 63/64th of the gas in a call and thus
|
||||
even if the called contract goes out of gas, the caller still
|
||||
has some gas left.
|
||||
has some gas left.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -328,4 +328,4 @@ invalid bids.
|
||||
highestBidder = bidder;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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