Merge pull request #9486 from ethereum/breaking

Merge breaking into develop.
This commit is contained in:
chriseth 2020-07-23 13:17:41 +02:00 committed by GitHub
commit 2fece0724a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
761 changed files with 3568 additions and 3500 deletions

View File

@ -10,7 +10,7 @@ include(EthPolicy)
eth_policy() eth_policy()
# project name and version should be set after cmake_policy CMP0048 # 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 # OSX target needed in order to support std::visit
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)

View File

@ -1,3 +1,39 @@
### 0.7.0 (unreleased)
Breaking changes:
* Type Checker: Disallow virtual for library functions.
* Constructors should not have visibility.
* Deprecated dot syntax for `value` and `gas`.
* Deprecated the identifier `now`.
* Disallow `gwei` as identifier.
* JSON AST: Removes members with ``null`` value from JSON output.
* Parser: NatSpec comments on variables are only allowed for public state variables.
* Type Checker: Disallow shifts by signed types.
* Type Checker: Exponentiation and shifts of literals by non-literals will always use ``uint256`` or ``int256`` as a type.
* Type Checker: Disallow structs and arrays in memory or calldata if they contain nested mappings.
* Type Checker: Disallow assignments to state variables that contain nested mappings.
* Type checker: Disallow events with same name and parameter types in inheritance hierarchy.
* ``using A for B`` only affects the contract it is mentioned in and not all derived contracts
* 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``.
* Remove the finney and szabo denominations.
Language Features:
* State mutability: Do not issue recommendation for stricter mutability for virtual functions but do issue it for functions that override.
* Yul: Disallow EVM instruction `pc()`.
* Yul: Disallow consecutive and trailing dots in identifiers. Leading dots were already disallowed.
* Inheritance: Allow overrides to have stricter state mutability: ``view`` can override ``nonpayable`` and ``pure`` can override ``view``.
Compiler Features:
* Variable declarations using the ``var`` keyword are not recognized anymore.
Bugfixes:
* NatSpec: Constructors and functions have consistent userdoc output.
* Inheritance: Disallow public state variables overwriting ``pure`` functions.
* State Mutability: Constant public state variables are considered ``pure`` functions.
### 0.6.12 (2020-07-22) ### 0.6.12 (2020-07-22)
Language Features: Language Features:
@ -29,6 +65,7 @@ Build System:
### 0.6.11 (2020-07-07) ### 0.6.11 (2020-07-07)
Language Features: Language Features:
* General: Add unit denomination ``gwei`` * General: Add unit denomination ``gwei``
* Yul: Support ``linkersymbol`` builtin in standalone assembly mode to refer to library addresses. * Yul: Support ``linkersymbol`` builtin in standalone assembly mode to refer to library addresses.

View File

@ -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: A "Hello World" program in Solidity is of even less use than in other languages, but still:
```solidity ```solidity
pragma solidity ^0.6.0; pragma solidity >=0.6.0 <0.8.0;
contract HelloWorld { contract HelloWorld {
function helloWorld() external pure returns (string memory) { function helloWorld() external pure returns (string memory) {

View File

@ -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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
interface OldContract { interface OldContract {
function someOldFunction(uint8 a) external; function someOldFunction(uint8 a) external;
function anotherOldFunction() external returns (bool); 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
interface OldContract { interface OldContract {
function someOldFunction(uint8 a) external; function someOldFunction(uint8 a) external;

View File

@ -0,0 +1,124 @@
********************************
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.
* 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: 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.

View File

@ -306,7 +306,7 @@ assemblyBlock
: '{' assemblyItem* '}' ; : '{' assemblyItem* '}' ;
assemblyExpression assemblyExpression
: assemblyCall | assemblyLiteral ; : assemblyCall | assemblyLiteral | assemblyIdentifier ;
assemblyCall assemblyCall
: ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ;
@ -318,7 +318,10 @@ assemblyAssignment
: assemblyIdentifierList ':=' assemblyExpression ; : assemblyIdentifierList ':=' assemblyExpression ;
assemblyIdentifierList assemblyIdentifierList
: identifier ( ',' identifier )* ; : assemblyIdentifier ( ',' assemblyIdentifier )* ;
assemblyIdentifier
: identifier ( '.' identifier )* ;
assemblyStackAssignment assemblyStackAssignment
: '=:' identifier ; : '=:' identifier ;
@ -358,11 +361,12 @@ assemblyType
subAssembly subAssembly
: 'assembly' identifier assemblyBlock ; : 'assembly' identifier assemblyBlock ;
// 'finney' and 'szabo' are no longer supported as denominations by latest Solidity.
numberLiteral numberLiteral
: (DecimalNumber | HexNumber) (NumberUnit | Gwei)?; : (DecimalNumber | HexNumber) (NumberUnit | Gwei | Finney | Szabo)?;
identifier identifier
: (Gwei | 'from' | 'calldata' | 'address' | Identifier) ; : (Gwei | Finney | Szabo | 'from' | 'calldata' | 'address' | Identifier) ;
BooleanLiteral BooleanLiteral
: 'true' | 'false' ; : 'true' | 'false' ;
@ -382,10 +386,12 @@ HexDigits
: HexCharacter ( '_'? HexCharacter )* ; : HexCharacter ( '_'? HexCharacter )* ;
NumberUnit NumberUnit
: 'wei' | 'szabo' | 'finney' | 'ether' : 'wei' | 'ether'
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ;
Gwei: 'gwei' ; Gwei: 'gwei' ;
Szabo: 'szabo' ;
Finney: 'finney' ;
HexLiteralFragment HexLiteralFragment
: 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ;

View File

@ -233,7 +233,7 @@ Given the contract:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract Foo { contract Foo {
function bar(bytes3[2] memory) public pure {} function bar(bytes3[2] memory) public pure {}
@ -537,11 +537,11 @@ For example,
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract Test { contract Test {
constructor() public { b = hex"12345678901234567890123456789012"; } constructor() { b = hex"12345678901234567890123456789012"; }
event Event(uint indexed a, bytes32 b); event Event(uint indexed a, bytes32 b);
event Event2(uint indexed a, bytes32 b); event Event2(uint indexed a, bytes32 b);
function foo(uint a) public { emit Event(a, 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 // 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; pragma experimental ABIEncoderV2;
contract Test { contract Test {

View File

@ -42,7 +42,7 @@ without a compiler change.
.. code:: .. code::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
library GetCode { library GetCode {
function at(address _addr) public view returns (bytes memory o_code) { function at(address _addr) public view returns (bytes memory o_code) {
@ -68,7 +68,7 @@ efficient code, for example:
.. code:: .. code::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
library VectorSum { 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. 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 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 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: Local Solidity variables are available for assignments, for example:
.. code:: .. code::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract C { contract C {
uint b; uint b;
@ -147,7 +148,7 @@ Local Solidity variables are available for assignments, for example:
assembly { assembly {
// We ignore the storage slot offset, we know it is zero // We ignore the storage slot offset, we know it is zero
// in this special case. // 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) }`` ``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`` Since Solidity 0.6.0 the name of a inline assembly variable may not
and it may not shadow any declaration visible in the scope of the inline assembly block 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 (including variable, contract and function declarations).
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.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 Assignments are possible to assembly-local variables and to function-local
variables. Take care that when you assign to variables that point to 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. 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. 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. 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, It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable,
though. though.

View File

@ -67,7 +67,7 @@ The following is the order of precedence for operators, listed in order of evalu
| *15* | Comma operator | ``,`` | | *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 Global Variables
================ ================
@ -91,7 +91,6 @@ Global Variables
- ``msg.data`` (``bytes``): complete calldata - ``msg.data`` (``bytes``): complete calldata
- ``msg.sender`` (``address payable``): sender of the message (current call) - ``msg.sender`` (``address payable``): sender of the message (current call)
- ``msg.value`` (``uint``): number of wei sent with the message - ``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.gasprice`` (``uint``): gas price of the transaction
- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) - ``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) - ``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>`. - ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
.. note:: .. 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. unless you know what you are doing.
Both the timestamp and the block hash can be influenced by miners to some degree. 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``, 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 ``msg.gas`` as alias for ``gasleft``, ``block.blockhash`` as alias for ``blockhash`` and
``sha3`` as alias for ``keccak256``. ``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 .. index:: visibility, public, private, external, internal

View File

@ -28,7 +28,7 @@ you receive the funds of the person who is now the richest.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract WithdrawalContract { contract WithdrawalContract {
address public richest; address public richest;
@ -36,7 +36,7 @@ you receive the funds of the person who is now the richest.
mapping (address => uint) pendingWithdrawals; mapping (address => uint) pendingWithdrawals;
constructor() public payable { constructor() payable {
richest = msg.sender; richest = msg.sender;
mostSent = msg.value; mostSent = msg.value;
} }
@ -62,13 +62,13 @@ This is as opposed to the more intuitive sending pattern:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract SendContract { contract SendContract {
address payable public richest; address payable public richest;
uint public mostSent; uint public mostSent;
constructor() public payable { constructor() payable {
richest = msg.sender; richest = msg.sender;
mostSent = msg.value; mostSent = msg.value;
} }
@ -124,14 +124,14 @@ restrictions highly readable.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract AccessRestriction { contract AccessRestriction {
// These will be assigned at the construction // These will be assigned at the construction
// phase, where `msg.sender` is the account // phase, where `msg.sender` is the account
// creating this contract. // creating this contract.
address public owner = msg.sender; address public owner = msg.sender;
uint public creationTime = now; uint public creationTime = block.timestamp;
// Modifiers can be used to change // Modifiers can be used to change
// the body of a function. // the body of a function.
@ -162,7 +162,7 @@ restrictions highly readable.
modifier onlyAfter(uint _time) { modifier onlyAfter(uint _time) {
require( require(
now >= _time, block.timestamp >= _time,
"Function called too early." "Function called too early."
); );
_; _;
@ -277,7 +277,7 @@ function finishes.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract StateMachine { contract StateMachine {
enum Stages { enum Stages {
@ -291,7 +291,7 @@ function finishes.
// This is the current stage. // This is the current stage.
Stages public stage = Stages.AcceptingBlindedBids; Stages public stage = Stages.AcceptingBlindedBids;
uint public creationTime = now; uint public creationTime = block.timestamp;
modifier atStage(Stages _stage) { modifier atStage(Stages _stage) {
require( require(
@ -310,10 +310,10 @@ function finishes.
// will not take the new stage into account. // will not take the new stage into account.
modifier timedTransitions() { modifier timedTransitions() {
if (stage == Stages.AcceptingBlindedBids && if (stage == Stages.AcceptingBlindedBids &&
now >= creationTime + 10 days) block.timestamp >= creationTime + 10 days)
nextStage(); nextStage();
if (stage == Stages.RevealBids && if (stage == Stages.RevealBids &&
now >= creationTime + 12 days) block.timestamp >= creationTime + 12 days)
nextStage(); nextStage();
// The other stages transition by transaction // The other stages transition by transaction
_; _;

View File

@ -14,7 +14,7 @@ defined as abstract, because the function ``utterance()`` was defined, but no im
provided (no implementation body ``{ }`` was given).:: provided (no implementation body ``{ }`` was given).::
// SPDX-License-Identifier: GPL-3.0 // 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 { abstract contract Feline {
function utterance() public virtual returns (bytes32); 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:: 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >=0.6.0 <0.8.0;
abstract contract Feline { abstract contract Feline {
function utterance() public virtual returns (bytes32); function utterance() public pure virtual returns (bytes32);
} }
contract Cat is Feline { 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 If a contract inherits from an abstract contract and does not implement all non-implemented

View File

@ -18,7 +18,7 @@ Not all types for constants and immutables are implemented at this time. The onl
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >0.6.4 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract C { contract C {
uint constant X = 32**22 + 8; uint constant X = 32**22 + 8;
@ -28,7 +28,7 @@ Not all types for constants and immutables are implemented at this time. The onl
uint immutable maxBalance; uint immutable maxBalance;
address immutable owner = msg.sender; address immutable owner = msg.sender;
constructor(uint _decimals, address _reference) public { constructor(uint _decimals, address _reference) {
decimals = _decimals; decimals = _decimals;
// Assignments to immutables can even access the environment. // Assignments to immutables can even access the environment.
maxBalance = _reference.balance; maxBalance = _reference.balance;
@ -45,7 +45,7 @@ Constant
For ``constant`` variables, the value has to be a constant at compile time and it has to be 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 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 ``block.number``) or
execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions 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 that might have a side-effect on memory allocation are allowed, but those that

View File

@ -35,7 +35,7 @@ This means that cyclic creation dependencies are impossible.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract OwnedToken { contract OwnedToken {
@ -48,7 +48,7 @@ This means that cyclic creation dependencies are impossible.
// This is the constructor which registers the // This is the constructor which registers the
// creator and the assigned name. // creator and the assigned name.
constructor(bytes32 _name) public { constructor(bytes32 _name) {
// State variables are accessed via their name // State variables are accessed via their name
// and not via e.g. `this.owner`. Functions can // and not via e.g. `this.owner`. Functions can
// be accessed directly or through `this.f`, // be accessed directly or through `this.f`,

View File

@ -66,7 +66,7 @@ is that they are cheaper to deploy and call.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.21 <0.7.0; pragma solidity >=0.4.21 <0.8.0;
contract ClientReceipt { contract ClientReceipt {
event Deposit( 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.10 <0.7.0; pragma solidity >=0.4.10 <0.8.0;
contract C { contract C {
function f() public payable { function f() public payable {

View File

@ -18,10 +18,10 @@ if they are marked ``virtual``. For details, please see
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract owned { contract owned {
constructor() public { owner = msg.sender; } constructor() { owner = msg.sender; }
address payable owner; address payable owner;
// This contract only defines a modifier but does not use // 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; mapping (address => bool) registeredAddresses;
uint price; uint price;
constructor(uint initialPrice) public { price = initialPrice; } constructor(uint initialPrice) { price = initialPrice; }
// It is important to also provide the // It is important to also provide the
// `payable` keyword here, otherwise the function will // `payable` keyword here, otherwise the function will

View File

@ -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:: with two integers, you would use something like the following::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract Simple { contract Simple {
uint sum; 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:: two integers passed as function parameters, then you use something like::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract Simple { contract Simple {
function arithmetic(uint _a, uint _b) function arithmetic(uint _a, uint _b)
@ -82,7 +82,7 @@ or you can provide return values
statement:: statement::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract Simple { contract Simple {
function arithmetic(uint _a, uint _b) function arithmetic(uint _a, uint _b)
@ -146,11 +146,11 @@ The following statements are considered modifying the state:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract C { contract C {
function f(uint a, uint b) public view returns (uint) { 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract C { contract C {
function f(uint a, uint b) public pure returns (uint) { 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 // 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 // This contract keeps all Ether sent to it with no way
// to get it back. // 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.7.0; pragma solidity >=0.6.2 <0.8.0;
contract Test { contract Test {
// This function is called for all messages sent to // 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract A { contract A {
function f(uint _in) public pure returns (uint out) { 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 // 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 // This will not compile
contract A { contract A {
@ -468,7 +468,7 @@ candidate, resolution fails.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract A { contract A {
function f(uint8 _in) public pure returns (uint8 out) { function f(uint8 _in) public pure returns (uint8 out) {

View File

@ -39,11 +39,11 @@ Details are given in the following example.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >0.6.99 <0.8.0;
contract Owned { contract Owned {
constructor() public { owner = msg.sender; } constructor() { owner = msg.sender; }
address payable owner; 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 // also a base class of `Destructible`, yet there is only a single
// instance of `owned` (as for virtual inheritance in C++). // instance of `owned` (as for virtual inheritance in C++).
contract Named is Owned, Destructible { contract Named is Owned, Destructible {
constructor(bytes32 name) public { constructor(bytes32 name) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name); 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 // If a constructor takes an argument, it needs to be
// provided in the header (or modifier-invocation-style at // provided in the header or modifier-invocation-style at
// the constructor of the derived contract (see below)). // the constructor of the derived contract (see below).
contract PriceFeed is Owned, Destructible, Named("GoldFeed") { contract PriceFeed is Owned, Destructible, Named("GoldFeed") {
function updateInfo(uint newInfo) public { function updateInfo(uint newInfo) public {
if (msg.sender == owner) info = newInfo; 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:: seen in the following example::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >0.6.99 <0.8.0;
contract owned { contract owned {
constructor() public { owner = msg.sender; } constructor() { owner = msg.sender; }
address payable owner; 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``:: ``Base1.destroy``. The way around this is to use ``super``::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract owned { contract owned {
constructor() public { owner = msg.sender; } constructor() { owner = msg.sender; }
address payable owner; address payable owner;
} }
@ -203,23 +203,29 @@ Function Overriding
Base functions can be overridden by inheriting contracts to change their Base functions can be overridden by inheriting contracts to change their
behavior if they are marked as ``virtual``. The overriding function must then 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract Base contract Base
{ {
function foo() virtual public {} function foo() virtual external view {}
} }
contract Middle is Base {} contract Middle is Base {}
contract Inherited is Middle 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 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
contract Base1 contract Base1
{ {
@ -259,7 +265,7 @@ that already overrides all other functions.
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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 A { function f() public pure{} }
contract B is A {} contract B is A {}
@ -300,11 +306,11 @@ of the variable:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
contract A contract A
{ {
function f() external pure virtual returns(uint) { return 5; } function f() external view virtual returns(uint) { return 5; }
} }
contract B is A 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
contract Base contract Base
{ {
@ -351,7 +357,7 @@ explicitly:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
contract Base1 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 It does not include the constructor code or internal functions that are
only called from the constructor. 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 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 // 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; uint public a;
constructor(uint _a) internal { constructor(uint _a) {
a = _a; a = _a;
} }
} }
contract B is A(1) { 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 :: .. warning ::
Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. 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. 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 .. 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:: derived contracts need to specify all of them. This can be done in two ways::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract Base { contract Base {
uint x; uint x;
constructor(uint _x) public { x = _x; } constructor(uint _x) { x = _x; }
} }
// Either directly specify in the inheritance list... // Either directly specify in the inheritance list...
contract Derived1 is Base(7) { contract Derived1 is Base(7) {
constructor() public {} constructor() {}
} }
// or through a "modifier" of the derived constructor. // or through a "modifier" of the derived constructor.
contract Derived2 is Base { 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 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 // 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 X {}
contract A is 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract Base1 { contract Base1 {
constructor() public {} constructor() {}
} }
contract Base2 { contract Base2 {
constructor() public {} constructor() {}
} }
// Constructors are executed in the following order: // Constructors are executed in the following order:
@ -526,7 +538,7 @@ One area where inheritance linearization is especially important and perhaps not
// 2 - Base2 // 2 - Base2
// 3 - Derived1 // 3 - Derived1
contract Derived1 is Base1, Base2 { contract Derived1 is Base1, Base2 {
constructor() public Base1() Base2() {} constructor() Base1() Base2() {}
} }
// Constructors are executed in the following order: // Constructors are executed in the following order:
@ -534,7 +546,7 @@ One area where inheritance linearization is especially important and perhaps not
// 2 - Base1 // 2 - Base1
// 3 - Derived2 // 3 - Derived2
contract Derived2 is Base2, Base1 { contract Derived2 is Base2, Base1 {
constructor() public Base2() Base1() {} constructor() Base2() Base1() {}
} }
// Constructors are still executed in the following order: // 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 // 2 - Base1
// 3 - Derived3 // 3 - Derived3
contract Derived3 is Base2, Base1 { contract Derived3 is Base2, Base1 {
constructor() public Base1() Base2() {} constructor() Base1() Base2() {}
} }

View File

@ -23,7 +23,7 @@ Interfaces are denoted by their own keyword:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.7.0; pragma solidity >=0.6.2 <0.8.0;
interface Token { interface Token {
enum TokenType { Fungible, NonFungible } enum TokenType { Fungible, NonFungible }
@ -44,7 +44,7 @@ inheritance.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.7.0; pragma solidity >=0.6.2 <0.8.0;
interface ParentA { interface ParentA {
function test() external returns (uint256); function test() external returns (uint256);

View File

@ -30,9 +30,8 @@ not possible to destroy a library.
Libraries can be seen as implicit base contracts of the contracts that use them. 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 They will not be explicitly visible in the inheritance hierarchy, but calls
to library functions look just like calls to functions of explicit base to library functions look just like calls to functions of explicit base
contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, contracts (using qualified access like ``L.f()``).
``internal`` functions of libraries are visible in all contracts, just as Of course, calls to internal functions
if the library were a base contract. Of course, calls to internal functions
use the internal calling convention, which means that all internal types 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. 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 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 // 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 // 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
struct bigint { struct bigint {
uint[] limbs; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.14 <0.7.0; pragma solidity >=0.5.14 <0.8.0;
library L { library L {
function f(uint256) external {} function f(uint256) external {}

View File

@ -30,7 +30,7 @@ Let us rewrite the set example from the
:ref:`libraries` in this way:: :ref:`libraries` in this way::
// SPDX-License-Identifier: GPL-3.0 // 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 // 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:: It is also possible to extend elementary types in that way::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
library Search { library Search {
function indexOf(uint[] storage self, uint value) function indexOf(uint[] storage self, uint value)

View File

@ -55,7 +55,7 @@ return parameter list for functions.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract C { contract C {
function f(uint a) private pure returns (uint b) { return a + 1; } 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract C { contract C {
uint private data; uint private data;
@ -115,7 +115,7 @@ when they are declared.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract C { contract C {
uint public data = 42; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract C { contract C {
uint public data; uint public data;
@ -156,7 +156,7 @@ to write a function, for example:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract arrayExample { contract arrayExample {
// public state variable // public state variable
@ -183,7 +183,7 @@ The next example is more complex:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract Complex { contract Complex {
struct Data { struct Data {

View File

@ -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 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. 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 Running Documentation Tests
--------------------------- ---------------------------

View File

@ -42,7 +42,7 @@ Functions of the current contract can be called directly ("internally"), also re
this nonsensical example:: this nonsensical example::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract C { contract C {
function g(uint a) public pure returns (uint ret) { return a + f(); } 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.7.0; pragma solidity >=0.6.2 <0.8.0;
contract InfoFeed { contract InfoFeed {
function info() public payable returns (uint ret) { return 42; } 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. so your contract is not vulnerable to a reentrancy exploit.
.. note:: .. note::
Before Solidity 0.6.2, the recommended way to specify the value and gas Before Solidity 0.6.2, the recommended way to specify the value and gas was to
was to use ``f.value(x).gas(g)()``. This is still possible but deprecated use ``f.value(x).gas(g)()``. This was deprecated in Solidity 0.6.2 and is no
and will be removed with Solidity 0.7.0. longer possible since Solidity 0.7.0.
Named Calls and Anonymous Function Parameters 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract C { contract C {
mapping(uint => uint) data; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract C { contract C {
// omitted name for parameter // omitted name for parameter
@ -188,11 +188,11 @@ is compiled so recursive creation-dependencies are not possible.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract D { contract D {
uint public x; uint public x;
constructor(uint a) public payable { constructor(uint a) payable {
x = a; x = a;
} }
} }
@ -244,11 +244,11 @@ which only need to be created if there is a dispute.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.2 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract D { contract D {
uint public x; uint public x;
constructor(uint a) public { constructor(uint a) {
x = a; x = a;
} }
} }
@ -314,7 +314,7 @@ groupings of expressions.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract C { contract C {
uint index; uint index;
@ -360,7 +360,7 @@ because only a reference and not a copy is passed.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract C { contract C {
uint[20] x; uint[20] x;
@ -419,7 +419,7 @@ the two variables have the same name but disjoint scopes.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract C { contract C {
function minimalScoping() pure public { 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 // 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 // This will report a warning
contract C { contract C {
function f() pure public returns (uint) { 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 // 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 // This will not compile
contract C { contract C {
function f() pure public returns (uint) { function f() pure public returns (uint) {
@ -552,7 +552,7 @@ and ``assert`` for internal error checking.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract Sharer { contract Sharer {
function sendHalf(address payable addr) public payable returns (uint balance) { 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract VendingMachine { contract VendingMachine {
function buy(uint amount) public payable { 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 // 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); } 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: out-of-gas situation and not a deliberate error condition:
The caller always retains 63/64th of the gas in a call and thus 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 even if the called contract goes out of gas, the caller still
has some gas left. has some gas left.

View File

@ -25,7 +25,7 @@ to receive their money - contracts cannot activate themselves.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract SimpleAuction { contract SimpleAuction {
// Parameters of the auction. Times are either // Parameters of the auction. Times are either
@ -60,9 +60,9 @@ to receive their money - contracts cannot activate themselves.
constructor( constructor(
uint _biddingTime, uint _biddingTime,
address payable _beneficiary address payable _beneficiary
) public { ) {
beneficiary = _beneficiary; beneficiary = _beneficiary;
auctionEndTime = now + _biddingTime; auctionEndTime = block.timestamp + _biddingTime;
} }
/// Bid on the auction with the value sent /// 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 // Revert the call if the bidding
// period is over. // period is over.
require( require(
now <= auctionEndTime, block.timestamp <= auctionEndTime,
"Auction already ended." "Auction already ended."
); );
@ -141,7 +141,7 @@ to receive their money - contracts cannot activate themselves.
// external contracts. // external contracts.
// 1. Conditions // 1. Conditions
require(now >= auctionEndTime, "Auction not yet ended."); require(block.timestamp >= auctionEndTime, "Auction not yet ended.");
require(!ended, "auctionEnd has already been called."); require(!ended, "auctionEnd has already been called.");
// 2. Effects // 2. Effects
@ -186,7 +186,7 @@ invalid bids.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract BlindAuction { contract BlindAuction {
struct Bid { struct Bid {
@ -213,16 +213,16 @@ invalid bids.
/// functions. `onlyBefore` is applied to `bid` below: /// functions. `onlyBefore` is applied to `bid` below:
/// The new function body is the modifier's body where /// The new function body is the modifier's body where
/// `_` is replaced by the old function body. /// `_` is replaced by the old function body.
modifier onlyBefore(uint _time) { require(now < _time); _; } modifier onlyBefore(uint _time) { require(block.timestamp < _time); _; }
modifier onlyAfter(uint _time) { require(now > _time); _; } modifier onlyAfter(uint _time) { require(block.timestamp > _time); _; }
constructor( constructor(
uint _biddingTime, uint _biddingTime,
uint _revealTime, uint _revealTime,
address payable _beneficiary address payable _beneficiary
) public { ) {
beneficiary = _beneficiary; beneficiary = _beneficiary;
biddingEnd = now + _biddingTime; biddingEnd = block.timestamp + _biddingTime;
revealEnd = biddingEnd + _revealTime; revealEnd = biddingEnd + _revealTime;
} }
@ -328,4 +328,4 @@ invalid bids.
highestBidder = bidder; highestBidder = bidder;
return true; return true;
} }
} }

View File

@ -143,14 +143,14 @@ The full contract
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.24 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract ReceiverPays { contract ReceiverPays {
address owner = msg.sender; address owner = msg.sender;
mapping(uint256 => bool) usedNonces; mapping(uint256 => bool) usedNonces;
constructor() public payable {} constructor() payable {}
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public { function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
require(!usedNonces[nonce]); require(!usedNonces[nonce]);
@ -340,7 +340,7 @@ The full contract
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract SimplePaymentChannel { contract SimplePaymentChannel {
address payable public sender; // The account sending payments. 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. uint256 public expiration; // Timeout in case the recipient never closes.
constructor (address payable _recipient, uint256 duration) constructor (address payable _recipient, uint256 duration)
public
payable payable
{ {
sender = msg.sender; sender = msg.sender;
recipient = _recipient; recipient = _recipient;
expiration = now + duration; expiration = block.timestamp + duration;
} }
/// the recipient can close the channel at any time by presenting a /// 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, /// if the timeout is reached without the recipient closing the channel,
/// then the Ether is released back to the sender. /// then the Ether is released back to the sender.
function claimTimeout() public { function claimTimeout() public {
require(now >= expiration); require(block.timestamp >= expiration);
selfdestruct(sender); selfdestruct(sender);
} }

View File

@ -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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
library Balances { library Balances {
function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal {

View File

@ -26,7 +26,7 @@ you can use state machine-like constructs inside a contract.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract Purchase { contract Purchase {
uint public value; 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. // Ensure that `msg.value` is an even number.
// Division will truncate if it is an odd number. // Division will truncate if it is an odd number.
// Check via multiplication that it wasn't an odd number. // Check via multiplication that it wasn't an odd number.
constructor() public payable { constructor() payable {
seller = msg.sender; seller = msg.sender;
value = msg.value / 2; value = msg.value / 2;
require((2 * value) == msg.value, "Value has to be even."); require((2 * value) == msg.value, "Value has to be even.");

View File

@ -33,7 +33,7 @@ of votes.
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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. /// @title Voting with delegation.
contract Ballot { contract Ballot {
@ -63,7 +63,7 @@ of votes.
Proposal[] public proposals; Proposal[] public proposals;
/// Create a new ballot to choose one of `proposalNames`. /// Create a new ballot to choose one of `proposalNames`.
constructor(bytes32[] memory proposalNames) public { constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender; chairperson = msg.sender;
voters[chairperson].weight = 1; voters[chairperson].weight = 1;

View File

@ -127,6 +127,7 @@ Contents
050-breaking-changes.rst 050-breaking-changes.rst
060-breaking-changes.rst 060-breaking-changes.rst
070-breaking-changes.rst
natspec-format.rst natspec-format.rst
security-considerations.rst security-considerations.rst
resources.rst resources.rst

View File

@ -72,7 +72,7 @@ the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract C { contract C {
@ -173,7 +173,7 @@ value and reference types, types that are encoded packed, and nested types.
.. code:: .. code::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract A { contract A {
struct S { struct S {
uint128 a; uint128 a;

View File

@ -18,7 +18,7 @@ Storage Example
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract SimpleStorage { contract SimpleStorage {
uint storedData; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >0.5.99 <0.8.0;
contract Coin { contract Coin {
// The keyword "public" makes variables // 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 // Constructor code is only run when the contract
// is created // is created
constructor() public { constructor() {
minter = msg.sender; minter = msg.sender;
} }
@ -186,7 +186,7 @@ and any user interface calls the automatically generated ``balances`` function f
.. index:: coin .. 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 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 contract. The ``msg`` variable (together with ``tx`` and ``block``) is a
:ref:`special global variable <special-variables-functions>` that :ref:`special global variable <special-variables-functions>` that

View File

@ -317,7 +317,7 @@ for the two function parameters and two return variables.
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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. */ /** @title Shape calculator. */
contract ShapeCalculator { contract ShapeCalculator {

View File

@ -49,7 +49,7 @@ The following example shows a contract and a function using all available tags.
.. code:: solidity .. code:: solidity
// SPDX-License-Identifier: GPL-3.0 // 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 /// @title A simulator for trees
/// @author Larry A. Gardner /// @author Larry A. Gardner

View File

@ -59,7 +59,7 @@ complete contract):
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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 // THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract Fund { contract Fund {
@ -83,7 +83,7 @@ as it uses ``call`` which forwards all remaining gas by default:
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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 // THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract Fund { contract Fund {
@ -103,7 +103,7 @@ outlined further below:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.11 <0.7.0; pragma solidity >=0.4.11 <0.8.0;
contract Fund { contract Fund {
/// @dev Mapping of ether shares of the contract. /// @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 // 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 // THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet { contract TxUserWallet {
address owner; address owner;
constructor() public { constructor() {
owner = msg.sender; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >0.6.99 <0.8.0;
interface TxUserWallet { interface TxUserWallet {
function transferTo(address payable dest, uint amount) external; 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 { contract TxAttackWallet {
address payable owner; address payable owner;
constructor() public { constructor() {
owner = msg.sender; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
contract Map { contract Map {
mapping (uint => uint)[] array; mapping (uint => uint)[] array;

View File

@ -27,7 +27,7 @@ storage.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract SimpleStorage { contract SimpleStorage {
uint storedData; // State variable uint storedData; // State variable
@ -48,7 +48,7 @@ Functions are the executable units of code within a contract.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract SimpleAuction { contract SimpleAuction {
function bid() public payable { // Function function bid() public payable { // Function
@ -77,7 +77,7 @@ Like functions, modifiers can be :ref:`overridden <modifier-overriding>`.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract Purchase { contract Purchase {
address public seller; address public seller;
@ -105,7 +105,7 @@ Events are convenience interfaces with the EVM logging facilities.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.21 <0.7.0; pragma solidity >=0.4.21 <0.8.0;
contract SimpleAuction { contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract Ballot { contract Ballot {
struct Voter { // Struct 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract Purchase { contract Purchase {
enum State { Created, Locked, Inactive } // Enum enum State { Created, Locked, Inactive } // Enum

View File

@ -56,7 +56,7 @@ Surround top level declarations in solidity source with two blank lines.
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract A { contract A {
// ... // ...
@ -75,7 +75,7 @@ Yes::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract A { contract A {
// ... // ...
@ -95,7 +95,7 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >=0.6.0 <0.8.0;
abstract contract A { abstract contract A {
function spam() public virtual pure; function spam() public virtual pure;
@ -116,7 +116,7 @@ Yes::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // 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 { abstract contract A {
function spam() virtual pure public; function spam() virtual pure public;
@ -251,7 +251,7 @@ Import statements should always be placed at the top of the file.
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // 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"; import "./Owned.sol";
@ -266,7 +266,7 @@ Yes::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract A { contract A {
// ... // ...
@ -300,10 +300,10 @@ Within a grouping, place the ``view`` and ``pure`` functions last.
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >0.6.99 <0.8.0;
contract A { contract A {
constructor() public { constructor() {
// ... // ...
} }
@ -337,7 +337,7 @@ Yes::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.0; pragma solidity >0.6.99 <0.8.0;
contract A { contract A {
@ -357,7 +357,7 @@ No::
// Public functions // Public functions
// ... // ...
constructor() public { constructor() {
// ... // ...
} }
@ -445,7 +445,7 @@ should:
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract Coin { contract Coin {
struct Bank { struct Bank {
@ -457,7 +457,7 @@ Yes::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract Coin contract Coin
{ {
@ -758,19 +758,19 @@ manner as modifiers if the function declaration is long or hard to read.
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // 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 // Base contracts just to make this compile
contract B { contract B {
constructor(uint) public { constructor(uint) {
} }
} }
contract C { contract C {
constructor(uint, uint) public { constructor(uint, uint) {
} }
} }
contract D { contract D {
constructor(uint) public { constructor(uint) {
} }
} }
@ -781,7 +781,6 @@ Yes::
B(param1) B(param1)
C(param2, param3) C(param2, param3)
D(param4) D(param4)
public
{ {
// do something with param5 // do something with param5
x = param5; x = param5;
@ -791,24 +790,24 @@ Yes::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // 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 // Base contracts just to make this compile
contract B { contract B {
constructor(uint) public { constructor(uint) {
} }
} }
contract C { contract C {
constructor(uint, uint) public { constructor(uint, uint) {
} }
} }
contract D { contract D {
constructor(uint) public { constructor(uint) {
} }
} }
@ -819,8 +818,7 @@ No::
constructor(uint param1, uint param2, uint param3, uint param4, uint param5) constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1) B(param1)
C(param2, param3) C(param2, param3)
D(param4) D(param4) {
public {
x = param5; x = param5;
} }
} }
@ -832,8 +830,7 @@ No::
constructor(uint param1, uint param2, uint param3, uint param4, uint param5) constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1) B(param1)
C(param2, param3) C(param2, param3)
D(param4) D(param4) {
public {
x = param5; x = param5;
} }
} }
@ -1015,14 +1012,14 @@ As shown in the example below, if the contract name is ``Congress`` and the libr
Yes:: Yes::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >0.6.99 <0.8.0;
// Owned.sol // Owned.sol
contract Owned { contract Owned {
address public owner; address public owner;
constructor() public { constructor() {
owner = msg.sender; owner = msg.sender;
} }
@ -1039,7 +1036,7 @@ Yes::
and in ``Congress.sol``:: and in ``Congress.sol``::
// SPDX-License-Identifier: GPL-3.0 // 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"; import "./Owned.sol";
@ -1051,14 +1048,14 @@ and in ``Congress.sol``::
No:: No::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >0.6.99 <0.8.0;
// owned.sol // owned.sol
contract owned { contract owned {
address public owner; address public owner;
constructor() public { constructor() {
owner = msg.sender; owner = msg.sender;
} }
@ -1096,7 +1093,7 @@ Events should be named using the CapWords style. Examples: ``Deposit``, ``Transf
Function Names 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 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:: added looks like the one below::
// SPDX-License-Identifier: GPL-3.0 // 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 /// @author The Solidity Team

View File

@ -26,6 +26,7 @@ are allowed for state variables, as storage reference types
in functions, or as parameters for library functions. in functions, or as parameters for library functions.
They cannot be used as parameters or return parameters They cannot be used as parameters or return parameters
of contract functions that are publicly visible. 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 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. :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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract MappingExample { contract MappingExample {
mapping(address => uint) public balances; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract MappingExample { contract MappingExample {
@ -123,7 +124,7 @@ the ``sum`` function iterates over to sum all the values.
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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 IndexValue { uint keyIndex; uint value; }
struct KeyFlag { uint key; bool deleted; } struct KeyFlag { uint key; bool deleted; }

View File

@ -43,7 +43,7 @@ value it referred to previously.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract DeleteExample { contract DeleteExample {
uint data; uint data;

View File

@ -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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.7.0; pragma solidity >=0.5.0 <0.8.0;
contract C { contract C {
// The data location of x is storage. // 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract C { contract C {
function f(uint len) public pure { function f(uint len) public pure {
@ -206,7 +206,7 @@ the first element to ``uint``.
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
contract C { contract C {
function f() public pure { function f() public pure {
@ -223,7 +223,7 @@ memory arrays, i.e. the following is not possible:
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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. // This will not compile.
contract C { contract C {
@ -243,7 +243,7 @@ individual elements:
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.7.0; pragma solidity >=0.4.0 <0.8.0;
contract C { contract C {
function f() public pure { function f() public pure {
@ -301,7 +301,7 @@ Array Members
:: ::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.0 <0.8.0;
contract ArrayContract { contract ArrayContract {
uint[2**20] m_aLotOfIntegers; 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >0.6.99 <0.8.0;
contract Proxy { contract Proxy {
/// @dev Address of the client contract managed by proxy i.e., this contract /// @dev Address of the client contract managed by proxy i.e., this contract
address client; address client;
constructor(address _client) public { constructor(address _client) {
client = _client; client = _client;
} }
@ -478,7 +478,7 @@ shown in the following example:
:: ::
// SPDX-License-Identifier: GPL-3.0 // 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. // Defines a new type with two fields.
// Declaring a struct outside of a contract allows // 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) { function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
campaignID = numCampaigns++; // campaignID is return variable campaignID = numCampaigns++; // campaignID is return variable
// Creates new struct in memory and copies it to storage. // We cannot use "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)"
// We leave out the mapping type, because it is not valid in memory. // because the RHS creates a memory-struct "Campaign" that contains a mapping.
// If structs are copied (even from storage to storage), Campaign storage c = campaigns[campaignID];
// types that are not valid outside of storage (ex. mappings and array of mappings) c.beneficiary = beneficiary;
// are always omitted, because they cannot be enumerated. c.fundingGoal = goal;
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
} }
function contribute(uint campaignID) public payable { function contribute(uint campaignID) public payable {

View File

@ -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. 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 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 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). - 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:: .. warning::
Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``, 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) * 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). * 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. 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: 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 and exponentiation is disallowed if the exponent is fractional (because that might result in
a non-rational number). 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:: .. 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``. 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``.
@ -544,7 +549,7 @@ subsequent unsigned integer values starting from ``0``.
:: ::
// SPDX-License-Identifier: GPL-3.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 { contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
@ -645,18 +650,19 @@ External (or public) functions have the following members:
* ``.address`` returns the address of the contract of the function. * ``.address`` returns the address of the contract of the function.
* ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>` * ``.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. .. note::
See :ref:`External Function Calls <external-function-calls>` for more information. External (or public) functions used to have the additional members
* ``.value(uint)`` returns a callable function object which, when called, will ``.gas(uint)`` and ``.value(uint)``. These were deprecated in Solidity 0.6.2
send the specified amount of wei to the target function. Deprecated - use ``{value: ...}`` instead. and removed in Solidity 0.7.0. Instead use ``{gas: ...}`` and ``{value: ...}``
See :ref:`External Function Calls <external-function-calls>` for more information. 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:: Example that shows how to use the members::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; pragma solidity >=0.6.4 <0.8.0;
// This will report a warning
contract Example { contract Example {
function f() public payable returns (bytes4) { function f() public payable returns (bytes4) {
@ -665,16 +671,14 @@ Example that shows how to use the members::
} }
function g() public { function g() public {
this.f.gas(10).value(800)(); this.f{gas: 10, value: 800}();
// New syntax:
// this.f{gas: 10, value: 800}()
} }
} }
Example that shows how to use internal function types:: Example that shows how to use internal function types::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0; pragma solidity >=0.4.16 <0.8.0;
library ArrayUtils { library ArrayUtils {
// internal functions can be used in internal library functions because // internal functions can be used in internal library functions because
@ -732,7 +736,7 @@ Example that shows how to use internal function types::
Another example that uses external function types:: Another example that uses external function types::
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.7.0; pragma solidity >=0.4.22 <0.8.0;
contract Oracle { contract Oracle {

View File

@ -2,23 +2,23 @@
Units and Globally Available Variables Units and Globally Available Variables
************************************** **************************************
.. index:: wei, finney, szabo, ether .. index:: wei, finney, szabo, gwei, ether
Ether Units 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 wei == 1);
assert(1 gwei == 1e9); assert(1 gwei == 1e9);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18); assert(1 ether == 1e18);
The only effect of the subdenomination suffix is a multiplication by a power of ten. 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 .. 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:: interpret a function parameter in days, you can in the following way::
function f(uint start, uint daysAfter) public { 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 namespace and are mainly used to provide information about the blockchain
or are general-use utility functions. 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 Block and Transaction Properties
@ -79,7 +79,6 @@ Block and Transaction Properties
- ``msg.sender`` (``address payable``): sender of the message (current call) - ``msg.sender`` (``address payable``): sender of the message (current call)
- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
- ``msg.value`` (``uint``): number of wei sent with the message - ``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.gasprice`` (``uint``): gas price of the transaction
- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) - ``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. This includes calls to library functions.
.. note:: .. 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. unless you know what you are doing.
Both the timestamp and the block hash can be influenced by miners to some degree. 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 The function ``gasleft`` was previously known as ``msg.gas``, which was deprecated in
version 0.4.21 and removed in version 0.5.0. 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 .. index:: abi, encoding, packed
ABI Encoding and Decoding Functions ABI Encoding and Decoding Functions

View File

@ -566,27 +566,40 @@ the latest version of the compiler.
Available upgrade modules Available upgrade modules
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------+---------+--------------------------------------------------+ +----------------------------+---------+--------------------------------------------------+
| Module | Version | Description | | Module | Version | Description |
+=================+=========+==================================================+ +============================+=========+==================================================+
| ``constructor`` | 0.5.0 | Constructors must now be defined using the | | ``constructor`` | 0.5.0 | Constructors must now be defined using the |
| | | ``constructor`` keyword. | | | | ``constructor`` keyword. |
+-----------------+---------+--------------------------------------------------+ +----------------------------+---------+--------------------------------------------------+
| ``visibility`` | 0.5.0 | Explicit function visibility is now mandatory, | | ``visibility`` | 0.5.0 | Explicit function visibility is now mandatory, |
| | | defaults to ``public``. | | | | defaults to ``public``. |
+-----------------+---------+--------------------------------------------------+ +----------------------------+---------+--------------------------------------------------+
| ``abstract`` | 0.6.0 | The keyword ``abstract`` has to be used if a | | ``abstract`` | 0.6.0 | The keyword ``abstract`` has to be used if a |
| | | contract does not implement all its functions. | | | | contract does not implement all its functions. |
+-----------------+---------+--------------------------------------------------+ +----------------------------+---------+--------------------------------------------------+
| ``virtual`` | 0.6.0 | Functions without implementation outside an | | ``virtual`` | 0.6.0 | Functions without implementation outside an |
| | | interface have to be marked ``virtual``. | | | | interface have to be marked ``virtual``. |
+-----------------+---------+--------------------------------------------------+ +----------------------------+---------+--------------------------------------------------+
| ``override`` | 0.6.0 | When overriding a function or modifier, the new | | ``override`` | 0.6.0 | When overriding a function or modifier, the new |
| | | keyword ``override`` must be used. | | | | 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 Please read :doc:`0.5.0 release notes <050-breaking-changes>`,
:doc:`0.6.0 release notes <060-breaking-changes>` for further details. :doc:`0.6.0 release notes <060-breaking-changes>` and
:doc:`0.7.0 release notes <070-breaking-changes>` for further details.
Synopsis Synopsis
~~~~~~~~ ~~~~~~~~
@ -622,115 +635,88 @@ If you found a bug or if you have a feature request, please
Example 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 // SPDX-License-Identifier: GPL-3.0
pragma solidity >0.4.23 <0.5.0; contract C {
// FIXME: remove constructor visibility and make the contract abstract
contract Updateable { constructor() internal {}
function run() public view returns (bool);
function update() public;
} }
contract Upgradable { contract D {
function run() public view returns (bool); uint time;
function upgrade();
function f() public payable {
// FIXME: change now to block.timestamp
time = now;
}
} }
contract Source is Updateable, Upgradable { contract E {
function Source() public {} D d;
function run() // FIXME: remove constructor visibility
public constructor() public {}
view
returns (bool) {}
function update() {} function g() public {
function upgrade() {} // FIXME: change .value(5) => {value: 5}
d.f.value(5)();
}
} }
Required changes Required changes
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
To bring the contracts up to date with the current Solidity version, the The above contract will not compile starting from 0.7.0. To bring the contract up to date with the
following upgrade modules have to be executed: ``constructor``, current Solidity version, the following upgrade modules have to be executed:
``visibility``, ``abstract``, ``override`` and ``virtual``. Please read the ``constructor-visibility``, ``now`` and ``dotsyntax``. Please read the documentation on
documentation on :ref:`available modules <upgrade-modules>` for further details. :ref:`available modules <upgrade-modules>` for further details.
Running the upgrade Running the upgrade
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
In this example, all modules needed to upgrade the contracts above, It is recommended to explicitly specify the upgrade modules by using ``--modules`` argument.
are available and all of them are activated by default. Therefore you
do not need to specify the ``--modules`` option.
.. code-block:: none .. code-block:: none
$ solidity-upgrade Source.sol --dry-run $ solidity-upgrade --modules constructor-visibility,now,dotsyntax Source.sol
.. code-block:: none The command above applies all changes as shown below. Please review them carefully (the pragmas will
have to be updated manually.)
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.
.. code-block:: solidity .. code-block:: solidity
pragma solidity >0.6.99 <0.8.0;
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0; abstract contract C {
// FIXME: remove constructor visibility and make the contract abstract
abstract contract Updateable { constructor() {}
function run() public view virtual returns (bool);
function update() public virtual;
} }
abstract contract Upgradable { contract D {
function run() public view virtual returns (bool); uint time;
function upgrade() public virtual;
function f() public payable {
// FIXME: change now to block.timestamp
time = block.timestamp;
}
} }
contract Source is Updateable, Upgradable { contract E {
constructor() public {} D d;
function run() // FIXME: remove constructor visibility
public constructor() {}
view
override(Updateable,Upgradable)
returns (bool) {}
function update() public override {} function g() public {
function upgrade() public override {} // FIXME: change .value(5) => {value: 5}
d.f{value: 5}();
}
} }

View File

@ -189,15 +189,13 @@ namespace solidity::langutil
K(Throw, "throw", 0) \ K(Throw, "throw", 0) \
K(Type, "type", 0) \ K(Type, "type", 0) \
K(Using, "using", 0) \ K(Using, "using", 0) \
K(Var, "var", 0) \
K(View, "view", 0) \ K(View, "view", 0) \
K(Virtual, "virtual", 0) \ K(Virtual, "virtual", 0) \
K(While, "while", 0) \ K(While, "while", 0) \
\ \
/* Ether subdenominations */ \ /* Ether subdenominations */ \
K(SubWei, "wei", 0) \ K(SubWei, "wei", 0) \
K(SubSzabo, "szabo", 0) \ K(SubGwei, "gwei", 0) \
K(SubFinney, "finney", 0) \
K(SubEther, "ether", 0) \ K(SubEther, "ether", 0) \
K(SubSecond, "seconds", 0) \ K(SubSecond, "seconds", 0) \
K(SubMinute, "minutes", 0) \ K(SubMinute, "minutes", 0) \
@ -232,7 +230,6 @@ namespace solidity::langutil
\ \
/* Identifiers (not keywords or future reserved words). */ \ /* Identifiers (not keywords or future reserved words). */ \
T(Identifier, nullptr, 0) \ T(Identifier, nullptr, 0) \
T(SubGwei, "gwei", 0) \
\ \
/* Keywords reserved for future use. */ \ /* Keywords reserved for future use. */ \
K(After, "after", 0) \ K(After, "after", 0) \
@ -267,6 +264,7 @@ namespace solidity::langutil
K(Typedef, "typedef", 0) \ K(Typedef, "typedef", 0) \
K(TypeOf, "typeof", 0) \ K(TypeOf, "typeof", 0) \
K(Unchecked, "unchecked", 0) \ K(Unchecked, "unchecked", 0) \
K(Var, "var", 0) \
\ \
/* Illegal token - not able to scan. */ \ /* Illegal token - not able to scan. */ \
T(Illegal, "ILLEGAL", 0) \ T(Illegal, "ILLEGAL", 0) \
@ -307,13 +305,12 @@ namespace TokenTraits
constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } 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 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) return op == Token::Pure || op == Token::View || op == Token::Payable;
|| 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 isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; }
constexpr bool isReservedKeyword(Token op) { return (Token::After <= op && op <= Token::Unchecked); } constexpr bool isReservedKeyword(Token op) { return (Token::After <= op && op <= Token::Unchecked); }

View File

@ -122,8 +122,9 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr
/// Checks that two events with the same name defined in this contract have different /// Checks that two events with the same name defined in this contract have different
/// argument types /// argument types
map<string, vector<EventDefinition const*>> events; map<string, vector<EventDefinition const*>> events;
for (EventDefinition const* event: _contract.events()) for (auto const* contract: _contract.annotation().linearizedBaseContracts)
events[event->name()].push_back(event); for (EventDefinition const* event: contract->events())
events[event->name()].push_back(event);
findDuplicateDefinitions(events); findDuplicateDefinitions(events);
} }

View File

@ -113,18 +113,16 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
for (ASTPointer<VariableDeclaration> const& member: _struct.members()) for (ASTPointer<VariableDeclaration> const& member: _struct.members())
{ {
Type const* memberType = member->annotation().type; Type const* memberType = member->annotation().type;
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
{ if (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
if (arrayType->isDynamicallySized()) memberType = arrayType->finalBaseType(true);
break;
memberType = arrayType->baseType();
}
if (auto structType = dynamic_cast<StructType const*>(memberType)) if (auto structType = dynamic_cast<StructType const*>(memberType))
if (_cycleDetector.run(structType->structDefinition())) if (_cycleDetector.run(structType->structDefinition()))
return; 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."); m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition.");
return false; return false;
@ -296,15 +294,6 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
"The \"immutable\" keyword can only be used for state variables." "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; using Location = VariableDeclaration::Location;
Location varLoc = _variable.referenceLocation(); Location varLoc = _variable.referenceLocation();
DataLocation typeLoc = DataLocation::Memory; DataLocation typeLoc = DataLocation::Memory;
@ -385,7 +374,7 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set."); 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)) if (auto ref = dynamic_cast<ReferenceType const*>(type))
{ {
bool isPointer = !_variable.isStateVariable(); bool isPointer = !_variable.isStateVariable();

View File

@ -61,25 +61,12 @@ bool DocStringTagParser::visit(VariableDeclaration const& _variable)
{ {
if (_variable.isStateVariable()) 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()) if (_variable.isPublic())
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables"); parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables");
else else
{ parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables");
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."
);
} }
return false; return false;
} }
@ -139,8 +126,8 @@ void DocStringTagParser::handleCallable(
StructurallyDocumentedAnnotation& _annotation StructurallyDocumentedAnnotation& _annotation
) )
{ {
static set<string> const validEventTags = set<string>{"author", "dev", "notice", "return", "param"}; static set<string> const validEventTags = set<string>{"dev", "notice", "return", "param"};
static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param", "inheritdoc"}; static set<string> const validTags = set<string>{"dev", "notice", "return", "param", "inheritdoc"};
if (dynamic_cast<EventDefinition const*>(&_callable)) if (dynamic_cast<EventDefinition const*>(&_callable))
parseDocStrings(_node, _annotation, validEventTags, "events"); parseDocStrings(_node, _annotation, validEventTags, "events");
@ -148,13 +135,6 @@ void DocStringTagParser::handleCallable(
parseDocStrings(_node, _annotation, validTags, "functions"); parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _node, _annotation); 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( void DocStringTagParser::parseDocStrings(

View File

@ -290,7 +290,7 @@ StateMutability OverrideProxy::stateMutability() const
return std::visit(GenericVisitor{ return std::visit(GenericVisitor{
[&](FunctionDefinition const* _item) { return _item->stateMutability(); }, [&](FunctionDefinition const* _item) { return _item->stateMutability(); },
[&](ModifierDefinition const*) { solAssert(false, "Requested state mutability from modifier."); return 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); }, m_item);
} }
@ -585,29 +585,35 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
"Overridden " + _overriding.astNodeName() + " is here:" "Overridden " + _overriding.astNodeName() + " is here:"
); );
// This is only relevant for a function overriding a function. // Stricter mutability is always okay except when super is Payable
if (_overriding.isFunction()) if (
{ (_overriding.isFunction() || _overriding.isVariable()) &&
if (_overriding.stateMutability() != _super.stateMutability()) (
overrideError( _overriding.stateMutability() > _super.stateMutability() ||
_overriding, _super.stateMutability() == StateMutability::Payable
_super, ) &&
6959_error, _overriding.stateMutability() != _super.stateMutability()
"Overriding function changes state mutability from \"" + )
stateMutabilityToString(_super.stateMutability()) + overrideError(
"\" to \"" + _overriding,
stateMutabilityToString(_overriding.stateMutability()) + _super,
"\"." 6959_error,
); "Overriding " +
_overriding.astNodeName() +
" changes state mutability from \"" +
stateMutabilityToString(_super.stateMutability()) +
"\" to \"" +
stateMutabilityToString(_overriding.stateMutability()) +
"\"."
);
if (_overriding.unimplemented() && !_super.unimplemented()) if (_overriding.unimplemented() && !_super.unimplemented())
overrideError( overrideError(
_overriding, _overriding,
_super, _super,
4593_error, 4593_error,
"Overriding an implemented function with an unimplemented function is not allowed." "Overriding an implemented function with an unimplemented function is not allowed."
); );
}
} }
} }

View File

@ -34,15 +34,16 @@
#include <liblangutil/Exceptions.h> #include <liblangutil/Exceptions.h>
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <libsolutil/CommonData.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
using namespace std; using namespace std;
using namespace solidity;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::frontend;
namespace solidity::frontend
{
bool ReferencesResolver::resolve(ASTNode const& _root) bool ReferencesResolver::resolve(ASTNode const& _root)
{ {
@ -202,6 +203,10 @@ bool ReferencesResolver::visit(Return const& _return)
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) 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; bool wasInsideFunction = m_yulInsideFunction;
m_yulInsideFunction = true; m_yulInsideFunction = true;
this->operator()(_function.body); this->operator()(_function.body);
@ -210,9 +215,10 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
void ReferencesResolver::operator()(yul::Identifier const& _identifier) void ReferencesResolver::operator()(yul::Identifier const& _identifier)
{ {
bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), ".slot");
bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), ".offset");
// Could also use `pathFromCurrentScope`, split by '.'
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str());
if (isSlot || isOffset) if (isSlot || isOffset)
{ {
@ -222,19 +228,22 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return; return;
string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - (
isSlot ? isSlot ?
string("_slot").size() : string(".slot").size() :
string("_offset").size() string(".offset").size()
)); ));
if (realName.empty()) if (realName.empty())
{ {
m_errorReporter.declarationError( m_errorReporter.declarationError(
4794_error, 4794_error,
_identifier.location, _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; return;
} }
declarations = m_resolver.nameFromCurrentScope(realName); 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) if (declarations.size() > 1)
{ {
@ -246,7 +255,18 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return; return;
} }
else if (declarations.size() == 0) 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; return;
}
if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front())) if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front()))
if (var->isLocalVariable() && m_yulInsideFunction) if (var->isLocalVariable() && m_yulInsideFunction)
{ {
@ -267,18 +287,11 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
{ {
for (auto const& identifier: _varDecl.variables) for (auto const& identifier: _varDecl.variables)
{ {
bool isSlot = boost::algorithm::ends_with(identifier.name.str(), "_slot"); validateYulIdentifierName(identifier.name, identifier.location);
bool isOffset = boost::algorithm::ends_with(identifier.name.str(), "_offset");
string namePrefix = identifier.name.str().substr(0, identifier.name.str().find('.'));
if (isSlot || isOffset) if (
m_errorReporter.declarationError( auto declarations = m_resolver.nameFromCurrentScope(identifier.name.str());
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);
!declarations.empty() !declarations.empty()
) )
{ {
@ -290,8 +303,6 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
3859_error, 3859_error,
identifier.location, identifier.location,
ssl, 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." "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 '.'."
);
} }

View File

@ -92,6 +92,9 @@ private:
void resolveInheritDoc(StructuredDocumentation const& _documentation, StructurallyDocumentedAnnotation& _annotation); 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; langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;

View File

@ -302,7 +302,7 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
bool SyntaxChecker::visit(FunctionDefinition const& _function) bool SyntaxChecker::visit(FunctionDefinition const& _function)
{ {
if (_function.noVisibilitySpecified()) if (!_function.isConstructor() && _function.noVisibilitySpecified())
{ {
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
m_errorReporter.syntaxError( m_errorReporter.syntaxError(

View File

@ -327,10 +327,14 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{ {
if (_function.markedVirtual()) 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\""); 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."); 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()) if (_function.isPayable())
@ -340,45 +344,62 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); 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) vector<VariableDeclaration const*> internalParametersInConstructor;
{
solAssert(!iType.message().empty(), "Expected detailed error message!"); auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& _var) {
m_errorReporter.fatalTypeError(4103_error, var.location(), iType.message()); 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 ( if (
_function.isPublic() && _function.isConstructor() &&
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && _var.referenceLocation() == VariableDeclaration::Location::Storage &&
!typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction()) !m_currentContract->abstract()
) )
m_errorReporter.typeError( m_errorReporter.typeError(
4957_error, 3644_error,
var.location(), _var.location(),
"This type is only supported in ABIEncoderV2. " "This parameter has a type that can only be used internally. "
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature." "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()) for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
{ {
@ -390,6 +411,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
checkArgumentAndReturnParameter(*var); checkArgumentAndReturnParameter(*var);
var->accept(*this); var->accept(*this);
} }
set<Declaration const*> modifiers; set<Declaration const*> modifiers;
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers()) for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
{ {
@ -415,11 +437,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
if (_function.isImplemented()) if (_function.isImplemented())
m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); 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()) if (_function.isConstructor())
m_errorReporter.typeError(6482_error, _function.location(), "Constructor cannot be defined in interfaces."); 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) else if (m_currentContract->contractKind() == ContractKind::Library)
if (_function.isConstructor()) if (_function.isConstructor())
@ -446,8 +467,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable) 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 // type is filled either by ReferencesResolver directly from the type name or by
// TypeChecker at the VariableDeclarationStatement level. // 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."); m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
if (_variable.value()) 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); _variable.value()->accept(*this);
} }
else else
@ -501,9 +525,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
if (!_variable.isStateVariable()) if (!_variable.isStateVariable())
{ {
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) if (
if (!varType->canLiveOutsideStorage()) _variable.referenceLocation() == VariableDeclaration::Location::CallData ||
m_errorReporter.typeError(4061_error, _variable.location(), "Type " + varType->toString() + " is only valid in storage."); _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) else if (_variable.visibility() >= Visibility::Public)
{ {
@ -534,7 +565,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType)) if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
{ {
auto result = referenceType->validForLocation(referenceType->location()); auto result = referenceType->validForLocation(referenceType->location());
if (result && _variable.isPublicCallableParameter()) if (result && (_variable.isConstructorParameter() || _variable.isPublicCallableParameter()))
result = referenceType->validForLocation(DataLocation::CallData); result = referenceType->validForLocation(DataLocation::CallData);
if (!result) if (!result)
{ {
@ -560,15 +591,11 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
if (_variable.isStateVariable()) if (_variable.isStateVariable())
m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true)); m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true));
else else
m_errorReporter.warning( m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->canonicalName(), false));
2332_error,
_variable.typeName() ? _variable.typeName()->location() : _variable.location(),
collisionMessage(varType->canonicalName(), false)
);
} }
vector<Type const*> oversizedSubtypes = frontend::oversizedSubtypes(*varType); vector<Type const*> oversizedSubtypes = frontend::oversizedSubtypes(*varType);
for (Type const* subtype: oversizedSubtypes) 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; return false;
@ -646,8 +673,12 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
{ {
if (var->isIndexed()) if (var->isIndexed())
numIndexed++; numIndexed++;
if (!type(*var)->canLiveOutsideStorage()) if (type(*var)->containsNestedMapping())
m_errorReporter.typeError(3448_error, var->location(), "Type is required to live outside storage."); m_errorReporter.typeError(
3448_error,
var->location(),
"Type containing a (nested) mapping is not allowed as event parameter type."
);
if (!type(*var)->interfaceType(false)) if (!type(*var)->interfaceType(false))
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
if ( if (
@ -724,7 +755,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
} }
else if (requiresStorage) 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; return false;
} }
else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast<Literal const*>(var->value().get())) 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)) 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; return false;
} }
else if (_context == yul::IdentifierContext::LValue) else if (_context == yul::IdentifierContext::LValue)
@ -764,7 +795,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
} }
else if (identifierInfo.isOffset) 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; return false;
} }
else else
@ -773,12 +804,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
} }
else if (!var->isConstant() && var->isStateVariable()) 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; return false;
} }
else if (var->type()->dataStoredIn(DataLocation::Storage)) 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; return false;
} }
else if (var->type()->sizeOnStack() != 1) else if (var->type()->sizeOnStack() != 1)
@ -792,7 +823,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
} }
else if (requiresStorage) 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; return false;
} }
else if (_context == yul::IdentifierContext::LValue) 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."); 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) bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{ {
if (!_statement.initialValue()) if (!_statement.initialValue())
{ {
// No initial value is only permitted for single variables with specified type. // 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 (_statement.declarations().size() != 1 || !_statement.declarations().front())
{ {
if (std::all_of( solAssert(m_errorReporter.hasErrors(), "");
_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(), "");
// It is okay to return here, as there are no named components on the // It is okay to return here, as there are no named components on the
// left-hand-side that could cause any damage later. // left-hand-side that could cause any damage later.
return false; 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.");
} }
VariableDeclaration const& varDecl = *_statement.declarations().front(); VariableDeclaration const& varDecl = *_statement.declarations().front();
if (!varDecl.annotation().type) solAssert(varDecl.annotation().type, "");
m_errorReporter.fatalTypeError(6983_error, _statement.location(), "Use of the \"var\" keyword is disallowed.");
if (dynamic_cast<MappingType const*>(type(varDecl))) if (dynamic_cast<MappingType const*>(type(varDecl)))
m_errorReporter.typeError( 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) for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
{ {
if (!variables[i]) if (!variables[i])
@ -1192,95 +1163,45 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
solAssert(!var.value(), "Value has to be tied to statement."); solAssert(!var.value(), "Value has to be tied to statement.");
TypePointer const& valueComponentType = valueTypes[i]; TypePointer const& valueComponentType = valueTypes[i];
solAssert(!!valueComponentType, ""); solAssert(!!valueComponentType, "");
if (!var.annotation().type) solAssert(var.annotation().type, "");
{
autoTypeDeductionNeeded = true;
// Infer type from value. var.accept(*this);
solAssert(!var.typeName(), ""); BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type);
var.annotation().type = valueComponentType->mobileType(); if (!result)
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); auto errorMsg = "Type " +
BoolResult result = valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type); valueComponentType->toString() +
if (!result) " 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 " + if (var.annotation().type->operator==(*valueComponentType->mobileType()))
valueComponentType->toString() + m_errorReporter.typeError(
" is not implicitly convertible to expected type " + 5107_error,
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,
_statement.location(), _statement.location(),
errorMsg + ".", errorMsg + ", but it can be explicitly converted."
result.message() );
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()); 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; return false;
} }
@ -1420,7 +1323,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
checkExpressionAssignment(*types[i], *tupleExpression->components()[i]); checkExpressionAssignment(*types[i], *tupleExpression->components()[i]);
} }
} }
else if (_type.category() == Type::Category::Mapping) else if (_type.nameable() && _type.containsNestedMapping())
{ {
bool isLocalOrReturn = false; bool isLocalOrReturn = false;
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression)) if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression))
@ -1428,7 +1331,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
if (variableDeclaration->isLocalOrReturn()) if (variableDeclaration->isLocalOrReturn())
isLocalOrReturn = true; isLocalOrReturn = true;
if (!isLocalOrReturn) 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(), _tuple.location(),
"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element." "Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element."
); );
else if (!inlineArrayType->canLiveOutsideStorage()) else if (inlineArrayType->containsNestedMapping())
m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage."); 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()); _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) void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function)
{ {
solAssert(_function.isConstructor(), ""); solAssert(_function.isConstructor(), "");
if (_function.markedVirtual())
m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual.");
if (_function.overrides()) if (_function.overrides())
m_errorReporter.typeError(1209_error, _function.location(), "Constructors cannot override."); m_errorReporter.typeError(1209_error, _function.location(), "Constructors cannot override.");
if (!_function.returnParameters().empty()) if (!_function.returnParameters().empty())
@ -1920,8 +1825,30 @@ void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function)
stateMutabilityToString(_function.stateMutability()) + stateMutabilityToString(_function.stateMutability()) +
"\"." "\"."
); );
if (_function.visibility() != Visibility::Public && _function.visibility() != Visibility::Internal) if (!_function.noVisibilitySpecified())
m_errorReporter.typeError(9239_error, _function.location(), "Constructor must be public or internal."); {
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( void TypeChecker::typeCheckABIEncodeFunctions(
@ -2066,24 +1993,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
toString(parameterTypes.size()) + toString(parameterTypes.size()) +
"."; ".";
// Extend error message in case we try to construct a struct with mapping member.
if (isStructConstructorCall) 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 }; return { isVariadic ? 1123_error : 9755_error, msg };
}
else if ( else if (
_functionType->kind() == FunctionType::Kind::BareCall || _functionType->kind() == FunctionType::Kind::BareCall ||
_functionType->kind() == FunctionType::Kind::BareCallCode || _functionType->kind() == FunctionType::Kind::BareCallCode ||
@ -2303,6 +2214,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (actualType->category() == Type::Category::Struct) 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(); functionType = dynamic_cast<StructType const&>(*actualType).constructorType();
funcCallAnno.kind = FunctionCallKind::StructConstructorCall; funcCallAnno.kind = FunctionCallKind::StructConstructorCall;
funcCallAnno.isPure = argumentsArePure; 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."); m_errorReporter.fatalTypeError(5540_error, _newExpression.location(), "Identifier is not a contract.");
if (contract->isInterface()) if (contract->isInterface())
m_errorReporter.fatalTypeError(2971_error, _newExpression.location(), "Cannot instantiate an interface."); 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()) if (contract->abstract())
m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); 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) else if (type->category() == Type::Category::Array)
{ {
if (!type->canLiveOutsideStorage()) if (type->containsNestedMapping())
m_errorReporter.fatalTypeError( m_errorReporter.fatalTypeError(
1164_error, 1164_error,
_newExpression.typeName().location(), _newExpression.typeName().location(),
"Type cannot live outside storage." "Array containing a (nested) mapping cannot be constructed in memory."
); );
if (!type->isDynamicallySized()) if (!type->isDynamicallySized())
m_errorReporter.typeError( m_errorReporter.typeError(
@ -2716,7 +2631,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
!annotation.referencedDeclaration && !annotation.referencedDeclaration &&
(memberName == "value" || memberName == "gas") (memberName == "value" || memberName == "gas")
) )
m_errorReporter.warning( m_errorReporter.typeError(
1621_error, 1621_error,
_memberAccess.location(), _memberAccess.location(),
"Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead." "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; return false;
} }

View File

@ -169,7 +169,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
!_funDef.isConstructor() && !_funDef.isConstructor() &&
!_funDef.isFallback() && !_funDef.isFallback() &&
!_funDef.isReceive() && !_funDef.isReceive() &&
!_funDef.overrides() !_funDef.virtualSemantics()
) )
m_errorReporter.warning( m_errorReporter.warning(
2018_error, 2018_error,

View File

@ -123,15 +123,9 @@ FunctionDefinition const* ContractDefinition::constructor() const
return nullptr; return nullptr;
} }
bool ContractDefinition::constructorIsPublic() const
{
FunctionDefinition const* f = constructor();
return !f || f->isPublic();
}
bool ContractDefinition::canBeDeployed() const bool ContractDefinition::canBeDeployed() const
{ {
return constructorIsPublic() && !abstract() && !isInterface(); return !abstract() && !isInterface();
} }
FunctionDefinition const* ContractDefinition::fallbackFunction() const FunctionDefinition const* ContractDefinition::fallbackFunction() const
@ -295,6 +289,12 @@ bool FunctionDefinition::libraryFunction() const
return false; return false;
} }
Visibility FunctionDefinition::defaultVisibility() const
{
solAssert(!isConstructor(), "");
return Declaration::defaultVisibility();
}
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
{ {
if (_internal) if (_internal)
@ -615,9 +615,8 @@ bool VariableDeclaration::isEventParameter() const
bool VariableDeclaration::hasReferenceOrMappingType() const bool VariableDeclaration::hasReferenceOrMappingType() const
{ {
solAssert(typeName(), ""); solAssert(typeName().annotation().type, "Can only be called after reference resolution");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution"); Type const* type = typeName().annotation().type;
Type const* type = typeName()->annotation().type;
return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(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()) else if (isCallableOrCatchParameter())
{ {
set<Location> locations{ Location::Memory }; set<Location> locations{ Location::Memory };
if (isInternalCallableParameter() || isLibraryFunctionParameter() || isTryCatchParameter()) if (
isConstructorParameter() ||
isInternalCallableParameter() ||
isLibraryFunctionParameter() ||
isTryCatchParameter()
)
locations.insert(Location::Storage); locations.insert(Location::Storage);
if (!isTryCatchParameter() && !isConstructorParameter()) if (!isTryCatchParameter() && !isConstructorParameter())
locations.insert(Location::CallData); locations.insert(Location::CallData);
@ -638,22 +642,8 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
return locations; return locations;
} }
else if (isLocalVariable()) else if (isLocalVariable())
{ // Further restrictions will be imposed later on.
solAssert(typeName(), ""); return set<Location>{ Location::Memory, Location::Storage, Location::CallData };
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);
}
else else
// Struct members etc. // Struct members etc.
return set<Location>{ Location::Unspecified }; return set<Location>{ Location::Unspecified };

View File

@ -506,8 +506,6 @@ public:
/// Returns the constructor or nullptr if no constructor was specified. /// Returns the constructor or nullptr if no constructor was specified.
FunctionDefinition const* constructor() const; 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 /// @returns true iff the contract can be deployed, i.e. is not abstract and has a
/// public constructor. /// public constructor.
/// Should only be called after the type checker has run. /// Should only be called after the type checker has run.
@ -809,15 +807,16 @@ public:
bool isPayable() const { return m_stateMutability == StateMutability::Payable; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
Block const& body() const { solAssert(m_body, ""); return *m_body; } Block const& body() const { solAssert(m_body, ""); return *m_body; }
Visibility defaultVisibility() const override;
bool isVisibleInContract() const override bool isVisibleInContract() const override
{ {
return Declaration::isVisibleInContract() && isOrdinary(); return isOrdinary() && Declaration::isVisibleInContract();
} }
bool isVisibleViaContractTypeAccess() const override 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 /// @returns the external signature of the function
/// That consists of the name of the function followed by the types of the /// That consists of the name of the function followed by the types of the
@ -897,13 +896,16 @@ public:
m_isIndexed(_isIndexed), m_isIndexed(_isIndexed),
m_mutability(_mutability), m_mutability(_mutability),
m_overrides(std::move(_overrides)), m_overrides(std::move(_overrides)),
m_location(_referenceLocation) {} m_location(_referenceLocation)
{
solAssert(m_typeName, "");
}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const 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; } ASTPointer<Expression> const& value() const { return m_value; }
bool isLValue() const override; bool isLValue() const override;
@ -929,6 +931,7 @@ public:
/// @returns true if this variable is a parameter or return parameter of an internal function /// @returns true if this variable is a parameter or return parameter of an internal function
/// or a function type of internal visibility. /// or a function type of internal visibility.
bool isInternalCallableParameter() const; bool isInternalCallableParameter() const;
/// @returns true if this variable is the parameter of a constructor.
bool isConstructorParameter() const; bool isConstructorParameter() const;
/// @returns true iff this variable is a parameter(or return parameter of a library function /// @returns true iff this variable is a parameter(or return parameter of a library function
bool isLibraryFunctionParameter() const; bool isLibraryFunctionParameter() const;
@ -965,7 +968,7 @@ protected:
Visibility defaultVisibility() const override { return Visibility::Internal; } Visibility defaultVisibility() const override { return Visibility::Internal; }
private: 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 /// Initially assigned value, can be missing. For local variables, this is stored inside
/// VariableDeclarationStatement and not here. /// VariableDeclarationStatement and not here.
ASTPointer<Expression> m_value; ASTPointer<Expression> m_value;
@ -2087,8 +2090,6 @@ public:
None = static_cast<int>(Token::Illegal), None = static_cast<int>(Token::Illegal),
Wei = static_cast<int>(Token::SubWei), Wei = static_cast<int>(Token::SubWei),
Gwei = static_cast<int>(Token::SubGwei), Gwei = static_cast<int>(Token::SubGwei),
Szabo = static_cast<int>(Token::SubSzabo),
Finney = static_cast<int>(Token::SubFinney),
Ether = static_cast<int>(Token::SubEther), Ether = static_cast<int>(Token::SubEther),
Second = static_cast<int>(Token::SubSecond), Second = static_cast<int>(Token::SubSecond),
Minute = static_cast<int>(Token::SubMinute), Minute = static_cast<int>(Token::SubMinute),

View File

@ -139,6 +139,9 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation
/// recursion immediately raises an error. /// recursion immediately raises an error.
/// Will be filled in by the DeclarationTypeChecker. /// Will be filled in by the DeclarationTypeChecker.
std::optional<bool> recursive; 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 struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation

View File

@ -203,7 +203,7 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie
void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node) 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) Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node)
@ -351,12 +351,18 @@ bool ASTJsonConverter::visit(OverrideSpecifier const& _node)
bool ASTJsonConverter::visit(FunctionDefinition 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 = { std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue),
make_pair("kind", TokenTraits::toString(_node.kind())), make_pair("kind", TokenTraits::toString(_node.kind())),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), 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("virtual", _node.markedVirtual()),
make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue),
make_pair("parameters", toJson(_node.parameterList())), make_pair("parameters", toJson(_node.parameterList())),
@ -366,6 +372,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
make_pair("implemented", _node.isImplemented()), make_pair("implemented", _node.isImplemented()),
make_pair("scope", idOrNull(_node.scope())) make_pair("scope", idOrNull(_node.scope()))
}; };
if (_node.isPartOfExternalInterface()) if (_node.isPartOfExternalInterface())
attributes.emplace_back("functionSelector", _node.externalIdentifierHex()); attributes.emplace_back("functionSelector", _node.externalIdentifierHex());
if (!_node.annotation().baseFunctions.empty()) if (!_node.annotation().baseFunctions.empty())
@ -380,7 +387,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{ {
std::vector<pair<string, Json::Value>> attributes = { std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("typeName", toJsonOrNull(_node.typeName())), make_pair("typeName", toJson(_node.typeName())),
make_pair("constant", _node.isConstant()), make_pair("constant", _node.isConstant()),
make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())), make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())),
make_pair("stateVariable", _node.isStateVariable()), make_pair("stateVariable", _node.isStateVariable()),

View File

@ -400,7 +400,7 @@ ASTPointer<FunctionDefinition> ASTJsonImporter::createFunctionDefinition(Json::V
return createASTNode<FunctionDefinition>( return createASTNode<FunctionDefinition>(
_node, _node,
memberAsASTString(_node, "name"), memberAsASTString(_node, "name"),
visibility(_node), kind == Token::Constructor ? Visibility::Default : visibility(_node),
stateMutability(_node), stateMutability(_node),
kind, kind,
memberAsBool(_node, "virtual"), memberAsBool(_node, "virtual"),
@ -886,7 +886,8 @@ ASTPointer<StructuredDocumentation> ASTJsonImporter::createDocumentation(Json::V
Json::Value ASTJsonImporter::member(Json::Value const& _node, string const& _name) 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]; return _node[_name];
} }
@ -1004,10 +1005,6 @@ Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _no
return Literal::SubDenomination::Wei; return Literal::SubDenomination::Wei;
else if (subDenStr == "gwei") else if (subDenStr == "gwei")
return Literal::SubDenomination::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") else if (subDenStr == "ether")
return Literal::SubDenomination::Ether; return Literal::SubDenomination::Ether;
else if (subDenStr == "seconds") else if (subDenStr == "seconds")

View File

@ -64,7 +64,8 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node)
Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _name) 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]; return _node[_name];
} }

View File

@ -97,6 +97,7 @@ public:
static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); } static IntegerType const* uint(unsigned _bits) { return integer(_bits, IntegerType::Modifier::Unsigned); }
static IntegerType const* uint256() { return uint(256); } 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); static FixedPointType const* fixedPoint(unsigned m, unsigned n, FixedPointType::Modifier _modifier);

View File

@ -417,11 +417,9 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope)) if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope))
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes()); usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope)) else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
{ usingForDirectives +=
for (ContractDefinition const* contract: contract->annotation().linearizedBaseContracts) contract->usingForDirectives() +
usingForDirectives += contract->usingForDirectives(); ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes());
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes());
}
else else
solAssert(false, ""); solAssert(false, "");
@ -570,7 +568,7 @@ bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType)
if (_operator == Token::SHR) if (_operator == Token::SHR)
return false; return false;
else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType)) 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)) else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType))
return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned();
else else
@ -954,12 +952,6 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
case Literal::SubDenomination::Gwei: case Literal::SubDenomination::Gwei:
value *= bigint("1000000000"); value *= bigint("1000000000");
break; break;
case Literal::SubDenomination::Szabo:
value *= bigint("1000000000000");
break;
case Literal::SubDenomination::Finney:
value *= bigint("1000000000000000");
break;
case Literal::SubDenomination::Ether: case Literal::SubDenomination::Ether:
value *= bigint("1000000000000000000"); value *= bigint("1000000000000000000");
break; break;
@ -1057,10 +1049,38 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const*
{ {
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{ {
auto commonType = Type::commonType(this, _other); if (isFractional())
if (!commonType) return TypeResult::err("Fractional literals not supported.");
return nullptr; else if (!integerType())
return commonType->binaryOperatorResult(_operator, _other); 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()) else if (_other->category() != category())
return nullptr; return nullptr;
@ -2032,6 +2052,20 @@ TypeResult ArrayType::interfaceType(bool _inLibrary) const
return result; 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 u256 ArrayType::memoryDataSize() const
{ {
solAssert(!isDynamicallySized(), ""); solAssert(!isDynamicallySized(), "");
@ -2260,7 +2294,7 @@ unsigned StructType::calldataEncodedSize(bool) const
unsigned size = 0; unsigned size = 0;
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
// Struct members are always padded. // Struct members are always padded.
size += member.type->calldataEncodedSize(); size += member.type->calldataEncodedSize();
} }
@ -2275,7 +2309,7 @@ unsigned StructType::calldataEncodedTailSize() const
unsigned size = 0; unsigned size = 0;
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
// Struct members are always padded. // Struct members are always padded.
size += member.type->calldataHeadSize(); size += member.type->calldataHeadSize();
} }
@ -2287,7 +2321,7 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
unsigned offset = 0; unsigned offset = 0;
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
if (member.name == _member) if (member.name == _member)
return offset; return offset;
// Struct members are always padded. // Struct members are always padded.
@ -2332,6 +2366,42 @@ u256 StructType::storageSize() const
return max<u256>(1, members(nullptr).storageSize()); 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 StructType::toString(bool _short) const
{ {
string ret = "struct " + m_struct.annotation().canonicalName; string ret = "struct " + m_struct.annotation().canonicalName;
@ -2347,10 +2417,7 @@ MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const
{ {
TypePointer type = variable->annotation().type; TypePointer type = variable->annotation().type;
solAssert(type, ""); solAssert(type, "");
// If we are not in storage, skip all members that cannot live outside of storage, solAssert(!(location() != DataLocation::Storage && type->containsNestedMapping()), "");
// ex. mappings and array of mappings
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
members.emplace_back( members.emplace_back(
variable->name(), variable->name(),
copyForLocationIfReference(type), copyForLocationIfReference(type),
@ -2519,10 +2586,9 @@ FunctionTypePointer StructType::constructorType() const
{ {
TypePointers paramTypes; TypePointers paramTypes;
strings paramNames; strings paramNames;
solAssert(!containsNestedMapping(), "");
for (auto const& member: members(nullptr)) for (auto const& member: members(nullptr))
{ {
if (!member.type->canLiveOutsideStorage())
continue;
paramNames.push_back(member.name); paramNames.push_back(member.name);
paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type)); paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type));
} }
@ -2556,20 +2622,12 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
TypePointers StructType::memoryMemberTypes() const TypePointers StructType::memoryMemberTypes() const
{ {
solAssert(!containsNestedMapping(), "");
TypePointers types; TypePointers types;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
if (variable->annotation().type->canLiveOutsideStorage()) types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
return types;
}
set<string> StructType::membersMissingInMemory() const return types;
{
set<string> missing;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
if (!variable->annotation().type->canLiveOutsideStorage())
missing.insert(variable->name());
return missing;
} }
vector<tuple<string, TypePointer>> StructType::makeStackItems() const vector<tuple<string, TypePointer>> StructType::makeStackItems() const
@ -3716,7 +3774,10 @@ TypeResult MappingType::interfaceType(bool _inLibrary) const
} }
} }
else 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; return this;
} }

View File

@ -271,7 +271,11 @@ public:
/// Returns true if the type can be stored in storage. /// Returns true if the type can be stored in storage.
virtual bool canBeStored() const { return true; } virtual bool canBeStored() const { return true; }
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. /// 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, /// 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. /// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; } virtual bool isValueType() const { return false; }
@ -561,7 +565,6 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
u256 literalValue(Literal const* _literal) const override; u256 literalValue(Literal const* _literal) const override;
@ -622,7 +625,6 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
std::string toString(bool) const override; std::string toString(bool) const override;
TypePointer mobileType() const override; TypePointer mobileType() const override;
@ -794,8 +796,9 @@ public:
bool isDynamicallyEncoded() const override; bool isDynamicallyEncoded() const override;
bigint storageSizeUpperBound() const override; bigint storageSizeUpperBound() const override;
u256 storageSize() 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; } bool nameable() const override { return true; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override; std::string signatureInExternalFunction(bool _structsByName) const override;
@ -811,6 +814,7 @@ public:
/// @returns true if this is a string /// @returns true if this is a string
bool isString() const { return m_arrayKind == ArrayKind::String; } bool isString() const { return m_arrayKind == ArrayKind::String; }
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; } Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
Type const* finalBaseType(bool breakIfDynamicArrayType) const;
u256 const& length() const { return m_length; } u256 const& length() const { return m_length; }
u256 memoryDataSize() const override; u256 memoryDataSize() const override;
@ -855,7 +859,6 @@ public:
unsigned calldataEncodedTailSize() const override { return 32; } unsigned calldataEncodedTailSize() const override { return 32; }
bool isDynamicallySized() const override { return true; } bool isDynamicallySized() const override { return true; }
bool isDynamicallyEncoded() 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; std::string toString(bool _short) const override;
TypePointer mobileType() const override; TypePointer mobileType() const override;
@ -896,7 +899,6 @@ public:
} }
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; } bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
bool canLiveOutsideStorage() const override { return !isSuper(); }
bool isValueType() const override { return !isSuper(); } bool isValueType() const override { return !isSuper(); }
bool nameable() const override { return !isSuper(); } bool nameable() const override { return !isSuper(); }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
@ -959,7 +961,7 @@ public:
u256 memoryDataSize() const override; u256 memoryDataSize() const override;
bigint storageSizeUpperBound() const override; bigint storageSizeUpperBound() const override;
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return true; } bool containsNestedMapping() const override;
bool nameable() const override { return true; } bool nameable() const override { return true; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
@ -989,8 +991,6 @@ public:
/// @returns the vector of types of members available in memory. /// @returns the vector of types of members available in memory.
TypePointers memoryMemberTypes() const; 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; void clearCache() const override;
@ -1021,7 +1021,6 @@ public:
} }
unsigned storageBytes() const override; unsigned storageBytes() const override;
bool leftAligned() const override { return false; } bool leftAligned() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() const override; std::string canonicalName() const override;
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
@ -1061,7 +1060,6 @@ public:
std::string toString(bool) const override; std::string toString(bool) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
TypePointer mobileType() const override; TypePointer mobileType() const override;
/// Converts components to their temporary types and performs some wildcard matching. /// Converts components to their temporary types and performs some wildcard matching.
@ -1232,7 +1230,6 @@ public:
unsigned storageBytes() const override; unsigned storageBytes() const override;
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool nameable() const override; bool nameable() const override;
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
bool hasSimpleZeroValueInMemory() const override { return false; } bool hasSimpleZeroValueInMemory() const override { return false; }
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
TypePointer encodingType() const override; TypePointer encodingType() const override;
@ -1365,7 +1362,7 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
std::string canonicalName() 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; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
Type const* encodingType() const override; Type const* encodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override; TypeResult interfaceType(bool _inLibrary) const override;
@ -1400,7 +1397,6 @@ public:
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override; MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -1426,7 +1422,6 @@ public:
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
u256 storageSize() const override; u256 storageSize() const override;
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
@ -1453,7 +1448,6 @@ public:
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -1493,7 +1487,6 @@ public:
std::string richIdentifier() const override; std::string richIdentifier() const override;
bool operator==(Type const& _other) const override; bool operator==(Type const& _other) const override;
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override; MemberList::MemberMap nativeMembers(ASTNode const*) const override;
@ -1526,7 +1519,6 @@ public:
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
unsigned calldataEncodedSize(bool) const override { return 32; } unsigned calldataEncodedSize(bool) const override { return 32; }
bool canBeStored() const override { return false; } bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return false; }
bool isValueType() const override { return true; } bool isValueType() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool) const override { return "inaccessible dynamic type"; } std::string toString(bool) const override { return "inaccessible dynamic type"; }

View File

@ -861,8 +861,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
for (auto const& member: _to.members(nullptr)) for (auto const& member: _to.members(nullptr))
{ {
solAssert(member.type, ""); solAssert(member.type, "");
if (!member.type->canLiveOutsideStorage()) solAssert(!member.type->containsNestedMapping(), "");
continue;
TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false); TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
auto memberTypeFrom = _from.memberType(member.name); 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)) for (auto const& member: _type.members(nullptr))
{ {
solAssert(member.type, ""); solAssert(member.type, "");
solAssert(member.type->canLiveOutsideStorage(), ""); solAssert(!member.type->containsNestedMapping(), "");
auto decodingType = member.type->decodingType(); auto decodingType = member.type->decodingType();
solAssert(decodingType, ""); solAssert(decodingType, "");
bool dynamic = decodingType->isDynamicallyEncoded(); bool dynamic = decodingType->isDynamicallyEncoded();

View File

@ -1080,8 +1080,7 @@ void CompilerUtils::convertType(
// stack: <memory ptr> <source ref> <memory ptr> // stack: <memory ptr> <source ref> <memory ptr>
for (auto const& member: typeOnStack->members(nullptr)) for (auto const& member: typeOnStack->members(nullptr))
{ {
if (!member.type->canLiveOutsideStorage()) solAssert(!member.type->containsNestedMapping(), "");
continue;
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name); pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
_context << offsets.first << Instruction::DUP3 << Instruction::ADD; _context << offsets.first << Instruction::DUP3 << Instruction::ADD;
_context << u256(offsets.second); _context << u256(offsets.second);

View File

@ -1915,10 +1915,6 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper()) if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
m_context << Instruction::ADDRESS; m_context << Instruction::ADDRESS;
break; break;
case Type::Category::Integer:
// "now"
m_context << Instruction::TIMESTAMP;
break;
default: default:
break; break;
} }

View File

@ -356,13 +356,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
structType.structDefinition() == sourceType.structDefinition(), structType.structDefinition() == sourceType.structDefinition(),
"Struct assignment with conversion." "Struct assignment with conversion."
); );
solAssert(!structType.containsNestedMapping(), "");
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
for (auto const& member: structType.members(nullptr)) for (auto const& member: structType.members(nullptr))
{ {
// assign each member that can live outside of storage // assign each member that can live outside of storage
TypePointer const& memberType = member.type; TypePointer const& memberType = member.type;
if (!memberType->canLiveOutsideStorage()) solAssert(memberType->nameable(), "");
continue;
TypePointer sourceMemberType = sourceType.memberType(member.name); TypePointer sourceMemberType = sourceType.memberType(member.name);
if (sourceType.location() == DataLocation::Storage) if (sourceType.location() == DataLocation::Storage)
{ {

View File

@ -75,7 +75,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
abi.emplace(std::move(method)); abi.emplace(std::move(method));
} }
FunctionDefinition const* constructor = _contractDef.constructor(); FunctionDefinition const* constructor = _contractDef.constructor();
if (constructor && constructor->visibility() >= Visibility::Public) if (constructor && !_contractDef.abstract())
{ {
FunctionType constrType(*constructor); FunctionType constrType(*constructor);
FunctionType const* externalFunctionType = constrType.interfaceFunctionType(); FunctionType const* externalFunctionType = constrType.interfaceFunctionType();

View File

@ -47,8 +47,12 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
{ {
string const value = extractDoc(constructorDefinition->annotation().docTags, "notice"); string const value = extractDoc(constructorDefinition->annotation().docTags, "notice");
if (!value.empty()) if (!value.empty())
{
// add the constructor, only if we have any documentation to add // 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"); string notice = extractDoc(_contractDef.annotation().docTags, "notice");

View File

@ -465,14 +465,6 @@ StateMutability Parser::parseStateMutability()
case Token::Pure: case Token::Pure:
stateMutability = StateMutability::Pure; stateMutability = StateMutability::Pure;
break; 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: default:
solAssert(false, "Invalid state mutability specifier."); solAssert(false, "Invalid state mutability specifier.");
} }
@ -686,19 +678,13 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory = _lookAheadArrayType ? ASTNodeFactory nodeFactory = _lookAheadArrayType ?
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this); ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
ASTPointer<TypeName> type;
ASTPointer<StructuredDocumentation> const documentation = parseStructuredDocumentation(); ASTPointer<StructuredDocumentation> const documentation = parseStructuredDocumentation();
if (_lookAheadArrayType) ASTPointer<TypeName> type = _lookAheadArrayType ? _lookAheadArrayType : parseTypeName();
type = _lookAheadArrayType; nodeFactory.setEndPositionFromNode(type);
else
{
type = parseTypeName(_options.allowVar);
if (type != nullptr)
nodeFactory.setEndPositionFromNode(type);
}
if (!_options.isStateVariable && documentation != nullptr) 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) if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
fatalParserError( fatalParserError(
@ -762,8 +748,6 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
{ {
if (location != VariableDeclaration::Location::Unspecified) if (location != VariableDeclaration::Location::Unspecified)
parserError(3548_error, "Location already specified."); parserError(3548_error, "Location already specified.");
else if (!type)
parserError(7439_error, "Location specifier needs explicit type name.");
else else
{ {
switch (token) switch (token)
@ -790,10 +774,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
} }
if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier) if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier)
{
identifier = make_shared<ASTString>(""); identifier = make_shared<ASTString>("");
solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense
}
else else
{ {
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -917,7 +898,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective()
if (m_scanner->currentToken() == Token::Mul) if (m_scanner->currentToken() == Token::Mul)
m_scanner->next(); m_scanner->next();
else else
typeName = parseTypeName(false); typeName = parseTypeName();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::Semicolon); expectToken(Token::Semicolon);
return nodeFactory.createNode<UsingForDirective>(library, typeName); return nodeFactory.createNode<UsingForDirective>(library, typeName);
@ -980,7 +961,7 @@ ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTN
return type; return type;
} }
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) ASTPointer<TypeName> Parser::parseTypeName()
{ {
RecursionGuard recursionGuard(*this); RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
@ -998,7 +979,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
auto stateMutability = elemTypeName.token() == Token::Address auto stateMutability = elemTypeName.token() == Token::Address
? optional<StateMutability>{StateMutability::NonPayable} ? optional<StateMutability>{StateMutability::NonPayable}
: nullopt; : nullopt;
if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken()))
{ {
if (elemTypeName.token() == Token::Address) if (elemTypeName.token() == Token::Address)
{ {
@ -1013,12 +994,6 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
} }
type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability); 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) else if (token == Token::Function)
type = parseFunctionType(); type = parseFunctionType();
else if (token == Token::Mapping) else if (token == Token::Mapping)
@ -1028,9 +1003,10 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else else
fatalParserError(3546_error, "Expected type name"); fatalParserError(3546_error, "Expected type name");
if (type) solAssert(type, "");
// Parse "[...]" postfixes for arrays. // Parse "[...]" postfixes for arrays.
type = parseTypeNameSuffix(type, nodeFactory); type = parseTypeNameSuffix(type, nodeFactory);
return type; return type;
} }
@ -1071,8 +1047,7 @@ ASTPointer<Mapping> Parser::parseMapping()
else else
fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type"); fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type");
expectToken(Token::Arrow); expectToken(Token::Arrow);
bool const allowVar = false; ASTPointer<TypeName> valueType = parseTypeName();
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RParen); expectToken(Token::RParen);
return nodeFactory.createNode<Mapping>(keyType, valueType); return nodeFactory.createNode<Mapping>(keyType, valueType);
@ -1553,53 +1528,14 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
if (_lookAheadArrayType) if (_lookAheadArrayType)
nodeFactory.setLocation(_lookAheadArrayType->location()); nodeFactory.setLocation(_lookAheadArrayType->location());
VarDeclParserOptions options;
options.allowLocationSpecifier = true;
vector<ASTPointer<VariableDeclaration>> variables; vector<ASTPointer<VariableDeclaration>> variables;
variables.emplace_back(parseVariableDeclaration(options, _lookAheadArrayType));
nodeFactory.setEndPositionFromNode(variables.back());
ASTPointer<Expression> value; 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) if (m_scanner->currentToken() == Token::Assign)
{ {
m_scanner->next(); m_scanner->next();
@ -1713,11 +1649,8 @@ ASTPointer<Expression> Parser::parseLeftHandSideExpression(
else if (m_scanner->currentToken() == Token::New) else if (m_scanner->currentToken() == Token::New)
{ {
expectToken(Token::New); expectToken(Token::New);
ASTPointer<TypeName> typeName(parseTypeName(false)); ASTPointer<TypeName> typeName(parseTypeName());
if (typeName) nodeFactory.setEndPositionFromNode(typeName);
nodeFactory.setEndPositionFromNode(typeName);
else
nodeFactory.markEndPosition();
expression = nodeFactory.createNode<NewExpression>(typeName); expression = nodeFactory.createNode<NewExpression>(typeName);
} }
else if (m_scanner->currentToken() == Token::Payable) else if (m_scanner->currentToken() == Token::Payable)
@ -1826,16 +1759,11 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break; break;
case Token::Number: case Token::Number:
if ( if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()))
(m_scanner->peekNextToken() == Token::Identifier && m_scanner->peekLiteral() == "gwei") ||
TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())
)
{ {
ASTPointer<ASTString> literal = getLiteralAndAdvance(); ASTPointer<ASTString> literal = getLiteralAndAdvance();
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
Token actualToken = m_scanner->currentToken() == Token::Identifier ? Token::SubGwei : m_scanner->currentToken(); Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(actualToken);
m_scanner->next(); m_scanner->next();
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination); expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
} }
@ -2062,7 +1990,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
Token token(m_scanner->currentToken()); Token token(m_scanner->currentToken());
bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier); 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; return LookAheadInfo::VariableDeclaration;
if (mightBeTypeName) if (mightBeTypeName)
{ {
@ -2071,7 +1999,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
// kind of statement. This means, for example, that we do not allow type expressions of the form // kind of statement. This means, for example, that we do not allow type expressions of the form
// ``address payable;``. // ``address payable;``.
// If we want to change this in the future, we need to consider another scanner token here. // 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; return LookAheadInfo::VariableDeclaration;
if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next)) if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next))
return LookAheadInfo::VariableDeclaration; return LookAheadInfo::VariableDeclaration;

View File

@ -58,7 +58,6 @@ private:
// https://stackoverflow.com/questions/17430377 // https://stackoverflow.com/questions/17430377
VarDeclParserOptions() {} VarDeclParserOptions() {}
bool allowVar = false;
bool isStateVariable = false; bool isStateVariable = false;
bool allowIndexed = false; bool allowIndexed = false;
bool allowEmptyName = false; bool allowEmptyName = false;
@ -108,7 +107,7 @@ private:
ASTPointer<Identifier> parseIdentifier(); ASTPointer<Identifier> parseIdentifier();
ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName(); ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName();
ASTPointer<TypeName> parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory); ASTPointer<TypeName> parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory);
ASTPointer<TypeName> parseTypeName(bool _allowVar); ASTPointer<TypeName> parseTypeName();
ASTPointer<FunctionTypeName> parseFunctionType(); ASTPointer<FunctionTypeName> parseFunctionType();
ASTPointer<Mapping> parseMapping(); ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList( ASTPointer<ParameterList> parseParameterList(

View File

@ -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); 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 } // end anonymous namespace
Json::Value removeNullMembers(Json::Value _json)
{
removeNullMembersHelper(_json);
return _json;
}
string jsonPrettyPrint(Json::Value const& _input) string jsonPrettyPrint(Json::Value const& _input)
{ {
static map<string, Json::Value> settings{{"indentation", " "}, {"enableYAMLCompatibility", true}}; static map<string, Json::Value> settings{{"indentation", " "}, {"enableYAMLCompatibility", true}};

View File

@ -29,6 +29,9 @@
namespace solidity::util { 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 /// Serialise the JSON object (@a _input) with indentation
std::string jsonPrettyPrint(Json::Value const& _input); std::string jsonPrettyPrint(Json::Value const& _input);

View File

@ -209,7 +209,10 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
m_currentScope->insideFunction() m_currentScope->insideFunction()
); );
for (auto const& variable: _varDecl.variables) for (auto const& variable: _varDecl.variables)
{
expectValidIdentifier(variable.name, variable.location);
expectValidType(variable.type, variable.location); expectValidType(variable.type, variable.location);
}
if (_varDecl.value) if (_varDecl.value)
{ {
@ -249,11 +252,13 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl)
void AsmAnalyzer::operator()(FunctionDefinition const& _funDef) void AsmAnalyzer::operator()(FunctionDefinition const& _funDef)
{ {
yulAssert(!_funDef.name.empty(), ""); yulAssert(!_funDef.name.empty(), "");
expectValidIdentifier(_funDef.name, _funDef.location);
Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
yulAssert(virtualBlock, ""); yulAssert(virtualBlock, "");
Scope& varScope = scope(virtualBlock); Scope& varScope = scope(virtualBlock);
for (auto const& var: _funDef.parameters + _funDef.returnVariables) for (auto const& var: _funDef.parameters + _funDef.returnVariables)
{ {
expectValidIdentifier(var.name, var.location);
expectValidType(var.type, var.location); expectValidType(var.type, var.location);
m_activeVariables.insert(&std::get<Scope::Variable>(varScope.identifiers.at(var.name))); m_activeVariables.insert(&std::get<Scope::Variable>(varScope.identifiers.at(var.name)));
} }
@ -529,6 +534,26 @@ Scope& AsmAnalyzer::scope(Block const* _block)
return *scopePtr; 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) void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location)
{ {
if (!m_dialect.types.count(_type)) if (!m_dialect.types.count(_type))
@ -611,13 +636,15 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
errorForVM(7110_error, "only available for Constantinople-compatible"); errorForVM(7110_error, "only available for Constantinople-compatible");
else if (_instr == evmasm::Instruction::CHAINID && !m_evmVersion.hasChainID()) else if (_instr == evmasm::Instruction::CHAINID && !m_evmVersion.hasChainID())
errorForVM(1561_error, "only available for Istanbul-compatible"); errorForVM(1561_error, "only available for Istanbul-compatible");
else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance())
errorForVM(7721_error, "only available for Istanbul-compatible");
else if (_instr == evmasm::Instruction::PC) else if (_instr == evmasm::Instruction::PC)
m_errorReporter.warning( m_errorReporter.error(
2450_error, 2450_error,
Error::Type::SyntaxError,
_location, _location,
"The \"" + "PC instruction is a low-level EVM feature. "
boost::to_lower_copy(instructionInfo(_instr).name) + "Because of that PC is disallowed in strict assembly."
"\" instruction is deprecated and will be removed in the next breaking release."
); );
else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance()) else if (_instr == evmasm::Instruction::SELFBALANCE && !m_evmVersion.hasSelfBalance())
errorForVM(3672_error, "only available for Istanbul-compatible"); errorForVM(3672_error, "only available for Istanbul-compatible");

View File

@ -108,6 +108,7 @@ private:
void checkAssignment(Identifier const& _variable, YulString _valueType); void checkAssignment(Identifier const& _variable, YulString _valueType);
Scope& scope(Block const* _block); Scope& scope(Block const* _block);
void expectValidIdentifier(YulString _identifier, langutil::SourceLocation const& _location);
void expectValidType(YulString _type, langutil::SourceLocation const& _location); void expectValidType(YulString _type, langutil::SourceLocation const& _location);
void expectType(YulString _expectedType, YulString _givenType, langutil::SourceLocation const& _location); void expectType(YulString _expectedType, YulString _givenType, langutil::SourceLocation const& _location);

View File

@ -1024,11 +1024,6 @@ function sstore(x1, x2, x3, x4, y1, y2, y3, y4) {
eth.storageStore(0:i32, 32:i32) eth.storageStore(0:i32, 32:i32)
} }
// Needed?
function pc() -> z1, z2, z3, z4 {
// TODO implement
unreachable()
}
function gas() -> z1, z2, z3, z4 { function gas() -> z1, z2, z3, z4 {
z4 := eth.getGasLeft() z4 := eth.getGasLeft()
} }

View File

@ -442,7 +442,7 @@ void CommandLineInterface::handleABI(string const& _contract)
if (!m_args.count(g_argAbi)) if (!m_args.count(g_argAbi))
return; return;
string data = jsonCompactPrint(m_compiler->contractABI(_contract)); string data = jsonCompactPrint(removeNullMembers(m_compiler->contractABI(_contract)));
if (m_args.count(g_argOutputDir)) if (m_args.count(g_argOutputDir))
createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data); createFile(m_compiler->filesystemFriendlyName(_contract) + ".abi", data);
else else
@ -454,7 +454,7 @@ void CommandLineInterface::handleStorageLayout(string const& _contract)
if (!m_args.count(g_argStorageLayout)) if (!m_args.count(g_argStorageLayout))
return; return;
string data = jsonCompactPrint(m_compiler->storageLayout(_contract)); string data = jsonCompactPrint(removeNullMembers(m_compiler->storageLayout(_contract)));
if (m_args.count(g_argOutputDir)) if (m_args.count(g_argOutputDir))
createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data);
else else
@ -483,9 +483,11 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra
if (m_args.count(argName)) if (m_args.count(argName))
{ {
std::string output = jsonPrettyPrint( std::string output = jsonPrettyPrint(
_natspecDev ? removeNullMembers(
m_compiler->natspecDev(_contract) : _natspecDev ?
m_compiler->natspecUser(_contract) m_compiler->natspecDev(_contract) :
m_compiler->natspecUser(_contract)
)
); );
if (m_args.count(g_argOutputDir)) if (m_args.count(g_argOutputDir))
@ -1561,7 +1563,8 @@ void CommandLineInterface::handleCombinedJSON()
} }
} }
string json = m_args.count(g_argPrettyJson) ? jsonPrettyPrint(output) : jsonCompactPrint(output); string json = m_args.count(g_argPrettyJson) ? jsonPrettyPrint(removeNullMembers(std::move(output))) :
jsonCompactPrint(removeNullMembers(std::move(output)));
if (m_args.count(g_argOutputDir)) if (m_args.count(g_argOutputDir))
createJson("combined", json); createJson("combined", json);
@ -1877,7 +1880,7 @@ void CommandLineInterface::outputCompilationResults()
{ {
string ret; string ret;
if (m_args.count(g_argAsmJson)) if (m_args.count(g_argAsmJson))
ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract)); ret = jsonPrettyPrint(removeNullMembers(m_compiler->assemblyJSON(contract)));
else else
ret = m_compiler->assemblyString(contract, m_sourceCodes); ret = m_compiler->assemblyString(contract, m_sourceCodes);

View File

@ -46,13 +46,9 @@ using rational = boost::rational<bigint>;
/// @NOTE This is not endian-specific; it's just a bunch of bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Address = util::h160; using Address = util::h160;
// The various denominations; here for ease of use where needed within code. // The ether and gwei denominations; here for ease of use where needed within code.
static const u256 wei = 1; static const u256 gwei = u256(1) << 9;
static const u256 shannon = u256("1000000000"); static const u256 ether = u256(1) << 18;
static const u256 gwei = shannon;
static const u256 szabo = shannon * 1000;
static const u256 finney = szabo * 1000;
static const u256 ether = finney * 1000;
class ExecutionFramework class ExecutionFramework
{ {
@ -288,7 +284,7 @@ protected:
bool m_transactionSuccessful = true; bool m_transactionSuccessful = true;
Address m_sender = account(0); Address m_sender = account(0);
Address m_contractAddress; Address m_contractAddress;
u256 const m_gasPrice = 100 * szabo; u256 const m_gasPrice = 10 * gwei;
u256 const m_gas = 100000000; u256 const m_gas = 100000000;
bytes m_output; bytes m_output;
u256 m_gasUsed; u256 m_gasUsed;

View File

@ -2,7 +2,7 @@
pragma solidity >=0.6.0; pragma solidity >=0.6.0;
contract C { contract C {
constructor() public {} constructor() {}
} }
contract D is C { contract D is C {
} }

View File

@ -5,10 +5,10 @@ Warning: Source file does not specify required compiler version!
--> message_format_utf8/input.sol --> message_format_utf8/input.sol
Warning: Statement has no effect. Warning: Statement has no effect.
--> message_format_utf8/input.sol:2:58: --> message_format_utf8/input.sol:2:51:
| |
2 | /* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; } 2 | /* ©©©©ᄅ©©©©© 2017 */ constructor () { "©©©©ᄅ©©©©©" ; }
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
Warning: Statement has no effect. Warning: Statement has no effect.
--> message_format_utf8/input.sol:6:25: --> message_format_utf8/input.sol:6:25:

View File

@ -1,5 +1,5 @@
contract Foo { contract Foo {
/* ©©©©ᄅ©©©©© 2017 */ constructor () public { "©©©©ᄅ©©©©©" ; } /* ©©©©ᄅ©©©©© 2017 */ constructor () { "©©©©ᄅ©©©©©" ; }
function f() public pure { function f() public pure {

View File

@ -3,7 +3,7 @@ pragma solidity >=0.0;
contract C contract C
{ {
constructor() public payable constructor() payable
{ {
int a; int a;

View File

@ -1,69 +1,69 @@
======= optimizer_user_yul/input.sol:C ======= ======= optimizer_user_yul/input.sol:C =======
EVM assembly: EVM assembly:
/* "optimizer_user_yul/input.sol":60:525 contract C... */ /* "optimizer_user_yul/input.sol":60:518 contract C... */
mstore(0x40, 0x80) mstore(0x40, 0x80)
/* "optimizer_user_yul/input.sol":108:113 int a */ /* "optimizer_user_yul/input.sol":101:106 int a */
0x00 0x00
/* "optimizer_user_yul/input.sol":188:197 let x,y,z */ /* "optimizer_user_yul/input.sol":181:190 let x,y,z */
dup1 dup1
0x00 0x00
dup1 dup1
/* "optimizer_user_yul/input.sol":212:213 1 */ /* "optimizer_user_yul/input.sol":205:206 1 */
0x01 0x01
/* "optimizer_user_yul/input.sol":209:210 0 */ /* "optimizer_user_yul/input.sol":202:203 0 */
0x00 0x00
/* "optimizer_user_yul/input.sol":202:214 sstore(0, 1) */ /* "optimizer_user_yul/input.sol":195:207 sstore(0, 1) */
sstore sstore
/* "optimizer_user_yul/input.sol":219:265 for { } sload(4) { } {... */ /* "optimizer_user_yul/input.sol":212:258 for { } sload(4) { } {... */
tag_3: tag_3:
/* "optimizer_user_yul/input.sol":233:234 4 */ /* "optimizer_user_yul/input.sol":226:227 4 */
0x04 0x04
/* "optimizer_user_yul/input.sol":227:235 sload(4) */ /* "optimizer_user_yul/input.sol":220:228 sload(4) */
sload sload
/* "optimizer_user_yul/input.sol":219:265 for { } sload(4) { } {... */ /* "optimizer_user_yul/input.sol":212:258 for { } sload(4) { } {... */
iszero iszero
tag_5 tag_5
jumpi jumpi
pop pop
/* "optimizer_user_yul/input.sol":251:260 exp(x, y) */ /* "optimizer_user_yul/input.sol":244:253 exp(x, y) */
dup1 dup1
dup3 dup3
exp exp
/* "optimizer_user_yul/input.sol":219:265 for { } sload(4) { } {... */ /* "optimizer_user_yul/input.sol":212:258 for { } sload(4) { } {... */
jump(tag_3) jump(tag_3)
tag_5: tag_5:
/* "optimizer_user_yul/input.sol":223:226 { } */ /* "optimizer_user_yul/input.sol":216:219 { } */
pop pop
pop pop
pop pop
/* "optimizer_user_yul/input.sol":275:276 2 */ /* "optimizer_user_yul/input.sol":268:269 2 */
0x02 0x02
/* "optimizer_user_yul/input.sol":270:276 a := 2 */ /* "optimizer_user_yul/input.sol":263:269 a := 2 */
swap1 swap1
pop pop
/* "optimizer_user_yul/input.sol":376:377 3 */ /* "optimizer_user_yul/input.sol":369:370 3 */
0x03 0x03
/* "optimizer_user_yul/input.sol":373:374 2 */ /* "optimizer_user_yul/input.sol":366:367 2 */
0x02 0x02
/* "optimizer_user_yul/input.sol":366:378 sstore(2, 3) */ /* "optimizer_user_yul/input.sol":359:371 sstore(2, 3) */
sstore sstore
/* "optimizer_user_yul/input.sol":383:516 for { } sload(5) { } {... */ /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */
tag_6: tag_6:
/* "optimizer_user_yul/input.sol":397:398 5 */ /* "optimizer_user_yul/input.sol":390:391 5 */
0x05 0x05
/* "optimizer_user_yul/input.sol":391:399 sload(5) */ /* "optimizer_user_yul/input.sol":384:392 sload(5) */
sload sload
tag_9 tag_9
jumpi jumpi
jump(tag_8) jump(tag_8)
tag_9: tag_9:
/* "optimizer_user_yul/input.sol":383:516 for { } sload(5) { } {... */ /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */
jump(tag_6) jump(tag_6)
tag_8: tag_8:
/* "optimizer_user_yul/input.sol":347:520 {... */ /* "optimizer_user_yul/input.sol":340:513 {... */
pop pop
/* "optimizer_user_yul/input.sol":60:525 contract C... */ /* "optimizer_user_yul/input.sol":60:518 contract C... */
dataSize(sub_0) dataSize(sub_0)
dup1 dup1
dataOffset(sub_0) dataOffset(sub_0)
@ -74,7 +74,7 @@ tag_8:
stop stop
sub_0: assembly { sub_0: assembly {
/* "optimizer_user_yul/input.sol":60:525 contract C... */ /* "optimizer_user_yul/input.sol":60:518 contract C... */
mstore(0x40, 0x80) mstore(0x40, 0x80)
0x00 0x00
dup1 dup1

View File

@ -2,7 +2,7 @@
pragma solidity >=0.0.0; pragma solidity >=0.0.0;
contract Error1 { contract Error1 {
constructor() public { constructor() {
balances[tx.origin] = ; // missing RHS. balances[tx.origin] = ; // missing RHS.
} }

View File

@ -45,7 +45,6 @@ JSON AST:
null null
], ],
"contractKind": "contract", "contractKind": "contract",
"documentation": null,
"fullyImplemented": true, "fullyImplemented": true,
"linearizedBaseContracts": "linearizedBaseContracts":
[ [
@ -59,7 +58,6 @@ JSON AST:
{ {
"attributes": "attributes":
{ {
"documentation": null,
"implemented": true, "implemented": true,
"isConstructor": true, "isConstructor": true,
"kind": "constructor", "kind": "constructor",
@ -68,7 +66,6 @@ JSON AST:
null null
], ],
"name": "", "name": "",
"overrides": null,
"scope": 18, "scope": 18,
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"virtual": false, "virtual": false,
@ -100,7 +97,7 @@ JSON AST:
"children": [], "children": [],
"id": 3, "id": 3,
"name": "ParameterList", "name": "ParameterList",
"src": "103:0:0" "src": "96:0:0"
}, },
{ {
"attributes": "attributes":
@ -113,17 +110,16 @@ JSON AST:
"children": [], "children": [],
"id": 8, "id": 8,
"name": "Block", "name": "Block",
"src": "103:49:0" "src": "96:49:0"
} }
], ],
"id": 9, "id": 9,
"name": "FunctionDefinition", "name": "FunctionDefinition",
"src": "82:70:0" "src": "82:63:0"
}, },
{ {
"attributes": "attributes":
{ {
"documentation": null,
"functionSelector": "af11c34c", "functionSelector": "af11c34c",
"implemented": true, "implemented": true,
"isConstructor": false, "isConstructor": false,
@ -133,7 +129,6 @@ JSON AST:
null null
], ],
"name": "five", "name": "five",
"overrides": null,
"scope": 18, "scope": 18,
"stateMutability": "view", "stateMutability": "view",
"virtual": false, "virtual": false,
@ -152,7 +147,7 @@ JSON AST:
"children": [], "children": [],
"id": 10, "id": 10,
"name": "ParameterList", "name": "ParameterList",
"src": "418:2:0" "src": "411:2:0"
}, },
{ {
"children": "children":
@ -163,12 +158,10 @@ JSON AST:
"constant": false, "constant": false,
"mutability": "mutable", "mutability": "mutable",
"name": "", "name": "",
"overrides": null,
"scope": 17, "scope": 17,
"stateVariable": false, "stateVariable": false,
"storageLocation": "default", "storageLocation": "default",
"type": "uint256", "type": "uint256",
"value": null,
"visibility": "internal" "visibility": "internal"
}, },
"children": "children":
@ -181,17 +174,17 @@ JSON AST:
}, },
"id": 11, "id": 11,
"name": "ElementaryTypeName", "name": "ElementaryTypeName",
"src": "441:4:0" "src": "434:4:0"
} }
], ],
"id": 12, "id": 12,
"name": "VariableDeclaration", "name": "VariableDeclaration",
"src": "441:4:0" "src": "434:4:0"
} }
], ],
"id": 13, "id": 13,
"name": "ParameterList", "name": "ParameterList",
"src": "440:6:0" "src": "433:6:0"
}, },
{ {
"children": "children":
@ -206,43 +199,41 @@ JSON AST:
{ {
"attributes": "attributes":
{ {
"argumentTypes": null,
"hexvalue": "35", "hexvalue": "35",
"isConstant": false, "isConstant": false,
"isLValue": false, "isLValue": false,
"isPure": true, "isPure": true,
"lValueRequested": false, "lValueRequested": false,
"subdenomination": null,
"token": "number", "token": "number",
"type": "int_const 5", "type": "int_const 5",
"value": "5" "value": "5"
}, },
"id": 14, "id": 14,
"name": "Literal", "name": "Literal",
"src": "460:1:0" "src": "453:1:0"
} }
], ],
"id": 15, "id": 15,
"name": "Return", "name": "Return",
"src": "453:8:0" "src": "446:8:0"
} }
], ],
"id": 16, "id": 16,
"name": "Block", "name": "Block",
"src": "447:19:0" "src": "440:19:0"
} }
], ],
"id": 17, "id": 17,
"name": "FunctionDefinition", "name": "FunctionDefinition",
"src": "405:61:0" "src": "398:61:0"
} }
], ],
"id": 18, "id": 18,
"name": "ContractDefinition", "name": "ContractDefinition",
"src": "62:406:0" "src": "62:399:0"
} }
], ],
"id": 19, "id": 19,
"name": "SourceUnit", "name": "SourceUnit",
"src": "36:433:0" "src": "36:426:0"
} }

View File

@ -4,7 +4,7 @@
{ {
"A": "A":
{ {
"content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}" "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}"
} }
} }
} }

View File

@ -1,10 +1,10 @@
{"errors":[{"component":"general","errorCode":"3364","formattedMessage":"A:2:112: DeclarationError: Base constructor arguments given twice. {"errors":[{"component":"general","errorCode":"3364","formattedMessage":"A:2:105: DeclarationError: Base constructor arguments given twice.
pragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}
^-------------------^ ^-------------------^
A:2:81: First constructor call is here: A:2:74: First constructor call is here:
pragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}
^--^ ^--^
A:2:104: Second constructor call is here: A:2:97: Second constructor call is here:
pragma solidity >=0.0; contract A { constructor(uint) public {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {}
^--^ ^--^
","message":"Base constructor arguments given twice.","secondarySourceLocations":[{"end":119,"file":"A","message":"First constructor call is here:","start":115},{"end":142,"file":"A","message":"Second constructor call is here:","start":138}],"severity":"error","sourceLocation":{"end":167,"file":"A","start":146},"type":"DeclarationError"}],"sources":{}} ","message":"Base constructor arguments given twice.","secondarySourceLocations":[{"end":112,"file":"A","message":"First constructor call is here:","start":108},{"end":135,"file":"A","message":"Second constructor call is here:","start":131}],"severity":"error","sourceLocation":{"end":160,"file":"A","start":139},"type":"DeclarationError"}],"sources":{}}

View File

@ -3,5 +3,5 @@ pragma solidity >=0.0;
contract C contract C
{ {
constructor() public {} constructor() {}
} }

View File

@ -104,7 +104,6 @@ contract MultiSigWallet {
/// @param _owners List of initial owners. /// @param _owners List of initial owners.
/// @param _required Number of required confirmations. /// @param _required Number of required confirmations.
constructor(address[] memory _owners, uint _required) constructor(address[] memory _owners, uint _required)
public
validRequirement(_owners.length, _required) validRequirement(_owners.length, _required)
{ {
for (uint i=0; i<_owners.length; i++) { for (uint i=0; i<_owners.length; i++) {
@ -227,7 +226,7 @@ contract MultiSigWallet {
{ {
if (isConfirmed(transactionId)) { if (isConfirmed(transactionId)) {
Transaction storage tx = transactions[transactionId]; Transaction storage tx = transactions[transactionId];
(tx.executed,) = tx.destination.call.value(tx.value)(tx.data); (tx.executed,) = tx.destination.call{value: tx.value}(tx.data);
if (tx.executed) if (tx.executed)
emit Execution(transactionId); emit Execution(transactionId);
else else

View File

@ -20,7 +20,6 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet {
/// @param _required Number of required confirmations. /// @param _required Number of required confirmations.
/// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis. /// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis.
constructor(address[] memory _owners, uint _required, uint _dailyLimit) constructor(address[] memory _owners, uint _required, uint _dailyLimit)
public
MultiSigWallet(_owners, _required) MultiSigWallet(_owners, _required)
{ {
dailyLimit = _dailyLimit; dailyLimit = _dailyLimit;
@ -48,7 +47,7 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet {
if (confirmed || tx.data.length == 0 && isUnderLimit(tx.value)) { if (confirmed || tx.data.length == 0 && isUnderLimit(tx.value)) {
if (!confirmed) if (!confirmed)
spentToday += tx.value; spentToday += tx.value;
(tx.executed,) = tx.destination.call.value(tx.value)(tx.data); (tx.executed,) = tx.destination.call{value: tx.value}(tx.data);
if (tx.executed) if (tx.executed)
emit Execution(transactionId); emit Execution(transactionId);
else { else {
@ -69,8 +68,8 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet {
internal internal
returns (bool) returns (bool)
{ {
if (now > lastDay + 24 hours) { if (block.timestamp > lastDay + 24 hours) {
lastDay = now; lastDay = block.timestamp;
spentToday = 0; spentToday = 0;
} }
if (spentToday + amount > dailyLimit || spentToday + amount < spentToday) if (spentToday + amount > dailyLimit || spentToday + amount < spentToday)
@ -88,7 +87,7 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet {
view view
returns (uint) returns (uint)
{ {
if (now > lastDay + 24 hours) if (block.timestamp > lastDay + 24 hours)
return dailyLimit; return dailyLimit;
if (dailyLimit < spentToday) if (dailyLimit < spentToday)
return 0; return 0;

View File

@ -50,7 +50,7 @@ contract ico is safeMath {
uint256 public totalMint; uint256 public totalMint;
uint256 public totalPremiumMint; uint256 public totalPremiumMint;
constructor(address payable foundation, address priceSet, uint256 exchangeRate, uint256 startBlockNum, address[] memory genesisAddr, uint256[] memory genesisValue) public { constructor(address payable foundation, address priceSet, uint256 exchangeRate, uint256 startBlockNum, address[] memory genesisAddr, uint256[] memory genesisValue) {
/* /*
Installation function. Installation function.

View File

@ -36,7 +36,7 @@ contract moduleHandler is multiOwner, announcementTypes {
uint256 debugModeUntil = block.number + 1000000; uint256 debugModeUntil = block.number + 1000000;
constructor(address[] memory newOwners) multiOwner(newOwners) public {} constructor(address[] memory newOwners) multiOwner(newOwners) {}
function load(address payable foundation, bool forReplace, address payable Token, address payable Premium, address payable Publisher, address payable Schelling, address payable Provider) public { function load(address payable foundation, bool forReplace, address payable Token, address payable Premium, address payable Publisher, address payable Schelling, address payable Provider) public {
/* /*
Loading modulest to ModuleHandler. Loading modulest to ModuleHandler.

View File

@ -12,7 +12,7 @@ contract multiOwner is safeMath {
/* /*
Constructor Constructor
*/ */
constructor(address[] memory newOwners) public { constructor(address[] memory newOwners) {
for ( uint256 a=0 ; a<newOwners.length ; a++ ) { for ( uint256 a=0 ; a<newOwners.length ; a++ ) {
_addOwner(newOwners[a]); _addOwner(newOwners[a]);
} }

View File

@ -11,6 +11,12 @@ contract thirdPartyPContractAbstract {
contract ptokenDB is tokenDB {} contract ptokenDB is tokenDB {}
/**
*
* @title Corion Platform Premium Token
* @author iFA @ Corion Platform
*
*/
contract premium is module, safeMath { contract premium is module, safeMath {
function replaceModule(address payable addr) external override returns (bool success) { function replaceModule(address payable addr) external override returns (bool success) {
require( super.isModuleHandler(msg.sender) ); require( super.isModuleHandler(msg.sender) );
@ -23,12 +29,6 @@ contract premium is module, safeMath {
require( _success && _active ); require( _success && _active );
_; _;
} }
/**
*
* @title Corion Platform Premium Token
* @author iFA @ Corion Platform
*
*/
string public name = "Corion Premium"; string public name = "Corion Premium";
string public symbol = "CORP"; string public symbol = "CORP";
@ -40,7 +40,7 @@ contract premium is module, safeMath {
mapping(address => bool) public genesis; mapping(address => bool) public genesis;
constructor(bool forReplace, address payable moduleHandler, address dbAddress, address icoContractAddr, address[] memory genesisAddr, uint256[] memory genesisValue) public { constructor(bool forReplace, address payable moduleHandler, address dbAddress, address icoContractAddr, address[] memory genesisAddr, uint256[] memory genesisValue) {
/* /*
Setup function. Setup function.
If an ICOaddress is defined then the balance of the genesis addresses will be set as well. If an ICOaddress is defined then the balance of the genesis addresses will be set as well.

View File

@ -118,7 +118,7 @@ contract provider is module, safeMath, announcementTypes {
uint256 private currentSchellingRound = 1; uint256 private currentSchellingRound = 1;
constructor(address payable _moduleHandler) public { constructor(address payable _moduleHandler) {
/* /*
Install function. Install function.
@ -256,7 +256,7 @@ contract provider is module, safeMath, announcementTypes {
providers[msg.sender].data[currHeight].country = country; providers[msg.sender].data[currHeight].country = country;
providers[msg.sender].data[currHeight].info = info; providers[msg.sender].data[currHeight].info = info;
providers[msg.sender].data[currHeight].currentRate = rate; providers[msg.sender].data[currHeight].currentRate = rate;
providers[msg.sender].data[currHeight].create = now; providers[msg.sender].data[currHeight].create = block.timestamp;
providers[msg.sender].data[currHeight].lastPaidRate = rate; providers[msg.sender].data[currHeight].lastPaidRate = rate;
providers[msg.sender].data[currHeight].priv = priv; providers[msg.sender].data[currHeight].priv = priv;
providers[msg.sender].data[currHeight].lastSupplyID = currentSchellingRound; providers[msg.sender].data[currHeight].lastSupplyID = currentSchellingRound;
@ -436,7 +436,7 @@ contract provider is module, safeMath, announcementTypes {
clients[msg.sender].lastSupplyID = currentSchellingRound; clients[msg.sender].lastSupplyID = currentSchellingRound;
clients[msg.sender].paidUpTo = currentSchellingRound; clients[msg.sender].paidUpTo = currentSchellingRound;
clients[msg.sender].lastRate = providers[provider].data[currHeight].currentRate; clients[msg.sender].lastRate = providers[provider].data[currHeight].currentRate;
clients[msg.sender].providerConnected = now; clients[msg.sender].providerConnected = block.timestamp;
emit ENewClient(msg.sender, provider, currHeight, bal); emit ENewClient(msg.sender, provider, currHeight, bal);
} }
function partProvider() isReady external { function partProvider() isReady external {

View File

@ -61,7 +61,7 @@ contract publisher is announcementTypes, module, safeMath {
mapping (address => uint256[]) public opponents; mapping (address => uint256[]) public opponents;
constructor(address payable moduleHandler) public { constructor(address payable moduleHandler) {
/* /*
Installation function. The installer will be registered in the admin list automatically Installation function. The installer will be registered in the admin list automatically

View File

@ -45,7 +45,7 @@ contract schellingDB is safeMath, schellingVars {
/* /*
Constructor Constructor
*/ */
constructor() public { constructor() {
rounds.push(); rounds.push();
rounds.push(); rounds.push();
rounds[0].blockHeight = block.number; rounds[0].blockHeight = block.number;
@ -249,7 +249,7 @@ contract schelling is module, announcementTypes, schellingVars {
bytes1 public belowChar = 0x30; bytes1 public belowChar = 0x30;
schellingDB private db; schellingDB private db;
constructor(address payable _moduleHandler, address _db, bool _forReplace) public { constructor(address payable _moduleHandler, address _db, bool _forReplace) {
/* /*
Installation function. Installation function.

View File

@ -11,6 +11,12 @@ contract thirdPartyContractAbstract {
function approvedCorionToken(address, uint256, bytes calldata) external returns (bool) {} function approvedCorionToken(address, uint256, bytes calldata) external returns (bool) {}
} }
/**
*
* @title Corion Platform Token
* @author iFA @ Corion Platform
*
*/
contract token is safeMath, module, announcementTypes { contract token is safeMath, module, announcementTypes {
/* /*
module callbacks module callbacks
@ -26,12 +32,7 @@ contract token is safeMath, module, announcementTypes {
require( _success && _active ); require( _success && _active );
_; _;
} }
/**
*
* @title Corion Platform Token
* @author iFA @ Corion Platform
*
*/
string public name = "Corion"; string public name = "Corion";
string public symbol = "COR"; string public symbol = "COR";
uint8 public decimals = 6; uint8 public decimals = 6;
@ -48,7 +49,7 @@ contract token is safeMath, module, announcementTypes {
mapping(address => bool) public genesis; mapping(address => bool) public genesis;
constructor(bool forReplace, address payable moduleHandler, address dbAddr, address payable icoContractAddr, address payable exchangeContractAddress, address payable[] memory genesisAddr, uint256[] memory genesisValue) public payable { constructor(bool forReplace, address payable moduleHandler, address dbAddr, address payable icoContractAddr, address payable exchangeContractAddress, address payable[] memory genesisAddr, uint256[] memory genesisValue) payable {
/* /*
Installation function Installation function

View File

@ -18,7 +18,6 @@ contract CategoricalEvent is Event {
Oracle _oracle, Oracle _oracle,
uint8 outcomeCount uint8 outcomeCount
) )
public
Event(_collateralToken, _oracle, outcomeCount) Event(_collateralToken, _oracle, outcomeCount)
{ {

View File

@ -34,7 +34,6 @@ abstract contract Event {
/// @param _oracle Oracle contract used to resolve the event /// @param _oracle Oracle contract used to resolve the event
/// @param outcomeCount Number of event outcomes /// @param outcomeCount Number of event outcomes
constructor(Token _collateralToken, Oracle _oracle, uint8 outcomeCount) constructor(Token _collateralToken, Oracle _oracle, uint8 outcomeCount)
public
{ {
// Validate input // Validate input
require(address(_collateralToken) != address(0) && address(_oracle) != address(0) && outcomeCount >= 2); require(address(_collateralToken) != address(0) && address(_oracle) != address(0) && outcomeCount >= 2);

View File

@ -34,7 +34,6 @@ contract ScalarEvent is Event {
int _lowerBound, int _lowerBound,
int _upperBound int _upperBound
) )
public
Event(_collateralToken, _oracle, 2) Event(_collateralToken, _oracle, 2)
{ {
// Validate bounds // Validate bounds

View File

@ -55,7 +55,7 @@ contract Campaign {
} }
modifier timedTransitions() { modifier timedTransitions() {
if (stage == Stages.AuctionStarted && deadline < now) if (stage == Stages.AuctionStarted && deadline < block.timestamp)
stage = Stages.AuctionFailed; stage = Stages.AuctionFailed;
_; _;
} }
@ -78,7 +78,6 @@ contract Campaign {
uint _funding, uint _funding,
uint _deadline uint _deadline
) )
public
{ {
// Validate input // Validate input
require( address(_eventContract) != address(0) require( address(_eventContract) != address(0)
@ -86,7 +85,7 @@ contract Campaign {
&& address(_marketMaker) != address(0) && address(_marketMaker) != address(0)
&& _fee < FEE_RANGE && _fee < FEE_RANGE
&& _funding > 0 && _funding > 0
&& now < _deadline); && block.timestamp < _deadline);
eventContract = _eventContract; eventContract = _eventContract;
marketFactory = _marketFactory; marketFactory = _marketFactory;
marketMaker = _marketMaker; marketMaker = _marketMaker;

Some files were not shown because too many files have changed in this diff Show More