Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth
2022-03-16 15:41:37 +01:00
542 changed files with 6696 additions and 2399 deletions
+18 -18
View File
@@ -45,10 +45,10 @@ Solidity language without a compiler change.
pragma solidity >=0.4.16 <0.9.0;
library GetCode {
function at(address _addr) public view returns (bytes memory code) {
function at(address addr) public view returns (bytes memory code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
let size := extcodesize(addr)
// allocate output byte array - this could also be done without assembly
// by using code = new bytes(size)
code := mload(0x40)
@@ -57,7 +57,7 @@ Solidity language without a compiler change.
// store length in memory
mstore(code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(code, 0x20), 0, size)
extcodecopy(addr, add(code, 0x20), 0, size)
}
}
}
@@ -74,43 +74,43 @@ efficient code, for example:
library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
function sumSolidity(uint[] memory _data) public pure returns (uint sum) {
for (uint i = 0; i < _data.length; ++i)
sum += _data[i];
function sumSolidity(uint[] memory data) public pure returns (uint sum) {
for (uint i = 0; i < data.length; ++i)
sum += data[i];
}
// We know that we only access the array in bounds, so we can avoid the check.
// 0x20 needs to be added to an array because the first slot contains the
// array length.
function sumAsm(uint[] memory _data) public pure returns (uint sum) {
for (uint i = 0; i < _data.length; ++i) {
function sumAsm(uint[] memory data) public pure returns (uint sum) {
for (uint i = 0; i < data.length; ++i) {
assembly {
sum := add(sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
sum := add(sum, mload(add(add(data, 0x20), mul(i, 0x20))))
}
}
}
// Same as above, but accomplish the entire code within inline assembly.
function sumPureAsm(uint[] memory _data) public pure returns (uint sum) {
function sumPureAsm(uint[] memory data) public pure returns (uint sum) {
assembly {
// Load the length (first 32 bytes)
let len := mload(_data)
let len := mload(data)
// Skip over the length field.
//
// Keep temporary variable so it can be incremented in place.
//
// NOTE: incrementing _data would result in an unusable
// _data variable after this assembly block
let data := add(_data, 0x20)
// NOTE: incrementing data would result in an unusable
// data variable after this assembly block
let dataElementLocation := add(data, 0x20)
// Iterate until the bound is not met.
for
{ let end := add(data, mul(len, 0x20)) }
lt(data, end)
{ data := add(data, 0x20) }
{ let end := add(dataElementLocation, mul(len, 0x20)) }
lt(dataElementLocation, end)
{ data := add(dataElementLocation, 0x20) }
{
sum := add(sum, mload(data))
sum := add(sum, mload(dataElementLocation))
}
}
}
+11 -1
View File
@@ -1,4 +1,15 @@
[
{
"uid": "SOL-2022-1",
"name": "AbiEncodeCallLiteralAsFixedBytesBug",
"summary": "Literals used for a fixed length bytes parameter in ``abi.encodeCall`` were encoded incorrectly.",
"description": "For the encoding, the compiler only considered the types of the expressions in the second argument of ``abi.encodeCall`` itself, but not the parameter types of the function given as first argument. In almost all cases the abi encoding of the type of the expression matches the abi encoding of the parameter type of the given function. This is because the type checker ensures the expression is implicitly convertible to the respective parameter type. However this is not true for number literals used for fixed bytes types shorter than 32 bytes, nor for string literals used for any fixed bytes type. Number literals were encoded as numbers instead of being shifted to become left-aligned. String literals were encoded as dynamically sized memory strings instead of being converted to a left-aligned bytes value.",
"link": "https://blog.soliditylang.org/2022/03/16/encodecall-bug/",
"introduced": "0.8.11",
"fixed": "0.8.13",
"severity": "very low"
},
{
"uid": "SOL-2021-4",
"name": "UserDefinedValueTypesBug",
@@ -8,7 +19,6 @@
"introduced": "0.8.8",
"fixed": "0.8.9",
"severity": "very low"
},
{
"uid": "SOL-2021-3",
+10 -2
View File
@@ -1549,13 +1549,21 @@
"released": "2021-11-09"
},
"0.8.11": {
"bugs": [],
"bugs": [
"AbiEncodeCallLiteralAsFixedBytesBug"
],
"released": "2021-12-20"
},
"0.8.12": {
"bugs": [],
"bugs": [
"AbiEncodeCallLiteralAsFixedBytesBug"
],
"released": "2022-02-16"
},
"0.8.13": {
"bugs": [],
"released": "2022-03-16"
},
"0.8.2": {
"bugs": [
"SignedImmutables",
+15 -15
View File
@@ -163,9 +163,9 @@ restrictions highly readable.
// prepend a check that only passes
// if the function is called from
// a certain address.
modifier onlyBy(address _account)
modifier onlyBy(address account)
{
if (msg.sender != _account)
if (msg.sender != account)
revert Unauthorized();
// Do not forget the "_;"! It will
// be replaced by the actual function
@@ -173,17 +173,17 @@ restrictions highly readable.
_;
}
/// Make `_newOwner` the new owner of this
/// Make `newOwner` the new owner of this
/// contract.
function changeOwner(address _newOwner)
function changeOwner(address newOwner)
public
onlyBy(owner)
{
owner = _newOwner;
owner = newOwner;
}
modifier onlyAfter(uint _time) {
if (block.timestamp < _time)
modifier onlyAfter(uint time) {
if (block.timestamp < time)
revert TooEarly();
_;
}
@@ -205,21 +205,21 @@ restrictions highly readable.
// refunded, but only after the function body.
// This was dangerous before Solidity version 0.4.0,
// where it was possible to skip the part after `_;`.
modifier costs(uint _amount) {
if (msg.value < _amount)
modifier costs(uint amount) {
if (msg.value < amount)
revert NotEnoughEther();
_;
if (msg.value > _amount)
payable(msg.sender).transfer(msg.value - _amount);
if (msg.value > amount)
payable(msg.sender).transfer(msg.value - amount);
}
function forceOwnerChange(address _newOwner)
function forceOwnerChange(address newOwner)
public
payable
costs(200 ether)
{
owner = _newOwner;
owner = newOwner;
// just some example condition
if (uint160(owner) & 0 == 1)
// This did not refund for Solidity
@@ -315,8 +315,8 @@ function finishes.
uint public creationTime = block.timestamp;
modifier atStage(Stages _stage) {
if (stage != _stage)
modifier atStage(Stages stage_) {
if (stage != stage_)
revert FunctionInvalidAtThisStage();
_;
}
+5 -5
View File
@@ -41,14 +41,14 @@ Not all types for constants and immutables are implemented at this time. The onl
uint immutable maxBalance;
address immutable owner = msg.sender;
constructor(uint _decimals, address _reference) {
decimals = _decimals;
constructor(uint decimals_, address ref) {
decimals = decimals_;
// Assignments to immutables can even access the environment.
maxBalance = _reference.balance;
maxBalance = ref.balance;
}
function isBalanceTooHigh(address _other) public view returns (bool) {
return _other.balance > maxBalance;
function isBalanceTooHigh(address other) public view returns (bool) {
return other.balance > maxBalance;
}
}
+2 -2
View File
@@ -48,7 +48,7 @@ This means that cyclic creation dependencies are impossible.
// This is the constructor which registers the
// creator and the assigned name.
constructor(bytes32 _name) {
constructor(bytes32 name_) {
// State variables are accessed via their name
// and not via e.g. `this.owner`. Functions can
// be accessed directly or through `this.f`,
@@ -65,7 +65,7 @@ This means that cyclic creation dependencies are impossible.
// no real way to verify that.
// This does not create a new contract.
creator = TokenCreator(msg.sender);
name = _name;
name = name_;
}
function changeName(bytes32 newName) public {
+8 -8
View File
@@ -80,18 +80,18 @@ four indexed arguments rather than three.
contract ClientReceipt {
event Deposit(
address indexed _from,
bytes32 indexed _id,
uint _value
address indexed from,
bytes32 indexed id,
uint value
);
function deposit(bytes32 _id) public payable {
function deposit(bytes32 id) public payable {
// Events are emitted using `emit`, followed by
// the name of the event and the arguments
// (if any) in parentheses. Any such invocation
// (even deeply nested) can be detected from
// the JavaScript API by filtering for `Deposit`.
emit Deposit(msg.sender, _id, msg.value);
emit Deposit(msg.sender, id, msg.value);
}
}
@@ -126,9 +126,9 @@ The output of the above looks like the following (trimmed):
{
"returnValues": {
"_from": "0x1111…FFFFCCCC",
"_id": "0x50…sd5adb20",
"_value": "0x420042"
"from": "0x1111…FFFFCCCC",
"id": "0x50…sd5adb20",
"value": "0x420042"
},
"raw": {
"data": "0x7f…91385",
+2 -2
View File
@@ -72,8 +72,8 @@ if they are marked ``virtual``. For details, please see
registeredAddresses[msg.sender] = true;
}
function changePrice(uint _price) public onlyOwner {
price = _price;
function changePrice(uint price_) public onlyOwner {
price = price_;
}
}
+29 -29
View File
@@ -17,17 +17,17 @@ that call them, similar to internal library functions.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;
function sum(uint[] memory _arr) pure returns (uint s) {
for (uint i = 0; i < _arr.length; i++)
s += _arr[i];
function sum(uint[] memory arr) pure returns (uint s) {
for (uint i = 0; i < arr.length; i++)
s += arr[i];
}
contract ArrayExample {
bool found;
function f(uint[] memory _arr) public {
function f(uint[] memory arr) public {
// This calls the free function internally.
// The compiler will add its code to the contract.
uint s = sum(_arr);
uint s = sum(arr);
require(s >= 10);
found = true;
}
@@ -65,8 +65,8 @@ with two integers, you would use something like the following:
contract Simple {
uint sum;
function taker(uint _a, uint _b) public {
sum = _a + _b;
function taker(uint a, uint b) public {
sum = a + b;
}
}
@@ -99,13 +99,13 @@ two integers passed as function parameters, then you use something like:
pragma solidity >=0.4.16 <0.9.0;
contract Simple {
function arithmetic(uint _a, uint _b)
function arithmetic(uint a, uint b)
public
pure
returns (uint sum, uint product)
{
sum = _a + _b;
product = _a * _b;
sum = a + b;
product = a * b;
}
}
@@ -126,12 +126,12 @@ statement:
pragma solidity >=0.4.16 <0.9.0;
contract Simple {
function arithmetic(uint _a, uint _b)
function arithmetic(uint a, uint b)
public
pure
returns (uint sum, uint product)
{
return (_a + _b, _a * _b);
return (a + b, a * b);
}
}
@@ -363,7 +363,7 @@ Fallback Function
-----------------
A contract can have at most one ``fallback`` function, declared using either ``fallback () external [payable]``
or ``fallback (bytes calldata _input) external [payable] returns (bytes memory _output)``
or ``fallback (bytes calldata input) external [payable] returns (bytes memory output)``
(both without the ``function`` keyword).
This function must have ``external`` visibility. A fallback function can be virtual, can override
and can have modifiers.
@@ -374,8 +374,8 @@ all and there is no :ref:`receive Ether function <receive-ether-function>`.
The fallback function always receives data, but in order to also receive Ether
it must be marked ``payable``.
If the version with parameters is used, ``_input`` will contain the full data sent to the contract
(equal to ``msg.data``) and can return data in ``_output``. The returned data will not be
If the version with parameters is used, ``input`` will contain the full data sent to the contract
(equal to ``msg.data``) and can return data in ``output``. The returned data will not be
ABI-encoded. Instead it will be returned without modifications (not even padding).
In the worst case, if a payable fallback function is also used in
@@ -398,7 +398,7 @@ operations as long as there is enough gas passed on to it.
for the function selector and then
you can use ``abi.decode`` together with the array slice syntax to
decode ABI-encoded data:
``(c, d) = abi.decode(_input[4:], (uint256, uint256));``
``(c, d) = abi.decode(input[4:], (uint256, uint256));``
Note that this should only be used as a last resort and
proper functions should be used instead.
@@ -487,13 +487,13 @@ The following example shows overloading of the function
pragma solidity >=0.4.16 <0.9.0;
contract A {
function f(uint _in) public pure returns (uint out) {
out = _in;
function f(uint value) public pure returns (uint out) {
out = value;
}
function f(uint _in, bool _really) public pure returns (uint out) {
if (_really)
out = _in;
function f(uint value, bool really) public pure returns (uint out) {
if (really)
out = value;
}
}
@@ -507,12 +507,12 @@ externally visible functions differ by their Solidity types but not by their ext
// This will not compile
contract A {
function f(B _in) public pure returns (B out) {
out = _in;
function f(B value) public pure returns (B out) {
out = value;
}
function f(address _in) public pure returns (address out) {
out = _in;
function f(address value) public pure returns (address out) {
out = value;
}
}
@@ -540,12 +540,12 @@ candidate, resolution fails.
pragma solidity >=0.4.16 <0.9.0;
contract A {
function f(uint8 _in) public pure returns (uint8 out) {
out = _in;
function f(uint8 val) public pure returns (uint8 out) {
out = val;
}
function f(uint256 _in) public pure returns (uint256 out) {
out = _in;
function f(uint256 val) public pure returns (uint256 out) {
out = val;
}
}
+5 -5
View File
@@ -421,8 +421,8 @@ equivalent to ``constructor() {}``. For example:
abstract contract A {
uint public a;
constructor(uint _a) {
a = _a;
constructor(uint a_) {
a = a_;
}
}
@@ -459,7 +459,7 @@ derived contracts need to specify all of them. This can be done in two ways:
contract Base {
uint x;
constructor(uint _x) { x = _x; }
constructor(uint x_) { x = x_; }
}
// Either directly specify in the inheritance list...
@@ -469,12 +469,12 @@ derived contracts need to specify all of them. This can be done in two ways:
// or through a "modifier" of the derived constructor.
contract Derived2 is Base {
constructor(uint _y) Base(_y * _y) {}
constructor(uint y) Base(y * y) {}
}
One way is directly in the inheritance list (``is Base(7)``). The other is in
the way a modifier is invoked as part of
the derived constructor (``Base(_y * _y)``). The first way to
the derived constructor (``Base(y * y)``). The first way to
do it is more convenient if the constructor argument is a
constant and defines the behaviour of the contract or
describes it. The second way has to be used if the
+1 -1
View File
@@ -10,7 +10,7 @@ Interfaces are similar to abstract contracts, but they cannot have any functions
There are further restrictions:
- They cannot inherit from other contracts, but they can inherit from other interfaces.
- All declared functions must be external.
- All declared functions must be external in the interface, even if they are public in the contract.
- They cannot declare a constructor.
- They cannot declare state variables.
- They cannot declare modifiers.
+8 -8
View File
@@ -146,16 +146,16 @@ custom types without the overhead of external function calls:
r.limbs[0] = x;
}
function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) {
r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length));
function add(bigint memory a, bigint memory b) internal pure returns (bigint memory r) {
r.limbs = new uint[](max(a.limbs.length, b.limbs.length));
uint carry = 0;
for (uint i = 0; i < r.limbs.length; ++i) {
uint a = limb(_a, i);
uint b = limb(_b, i);
uint limbA = limb(a, i);
uint limbB = limb(b, i);
unchecked {
r.limbs[i] = a + b + carry;
r.limbs[i] = limbA + limbB + carry;
if (a + b < a || (a + b == type(uint).max && carry > 0))
if (limbA + limbB < limbA || (limbA + limbB == type(uint).max && carry > 0))
carry = 1;
else
carry = 0;
@@ -172,8 +172,8 @@ custom types without the overhead of external function calls:
}
}
function limb(bigint memory _a, uint _limb) internal pure returns (uint) {
return _limb < _a.limbs.length ? _a.limbs[_limb] : 0;
function limb(bigint memory a, uint index) internal pure returns (uint) {
return index < a.limbs.length ? a.limbs[index] : 0;
}
function max(uint a, uint b) private pure returns (uint) {
+73 -47
View File
@@ -6,71 +6,96 @@
Using For
*********
The directive ``using A for B;`` can be used to attach library
functions (from the library ``A``) to any type (``B``)
in the context of a contract.
The directive ``using A for B;`` can be used to attach
functions (``A``) as member functions to any type (``B``).
These functions will receive the object they are called on
as their first parameter (like the ``self`` variable in Python).
The effect of ``using A for *;`` is that the functions from
the library ``A`` are attached to *any* type.
It is valid either at file level or inside a contract,
at contract level.
In both situations, *all* functions in the library are attached,
The first part, ``A``, can be one of:
- a list of file-level or library functions (``using {f, g, h, L.t} for uint;``) -
only those functions will be attached to the type.
- the name of a library (``using L for uint;``) -
all functions (both public and internal ones) of the library are attached to the type
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
Inside contracts, you can also use ``using L for *;``,
which has the effect that all functions of the library ``L``
are attached to *all* types.
If you specify a library, *all* functions in the library are attached,
even those where the type of the first parameter does not
match the type of the object. The type is checked at the
point the function is called and function overload
resolution is performed.
If you use a list of functions (``using {f, g, h, L.t} for uint;``),
then the type (``uint``) has to be implicitly convertible to the
first parameter of each of these functions. This check is
performed even if none of these functions are called.
The ``using A for B;`` directive is active only within the current
contract, including within all of its functions, and has no effect
outside of the contract in which it is used. The directive
may only be used inside a contract, not inside any of its functions.
scope (either the contract or the current module/source unit),
including within all of its functions, and has no effect
outside of the contract or module in which it is used.
When the directive is used at file level and applied to a
user-defined type which was defined at file level in the same file,
the word ``global`` can be added at the end. This will have the
effect that the functions are attached to the type everywhere
the type is available (including other files), not only in the
scope of the using statement.
Let us rewrite the set example from the
:ref:`libraries` in this way:
:ref:`libraries` section in this way, using file-level functions
instead of library functions.
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
pragma solidity ^0.8.13;
// This is the same code as before, just without comments
struct Data { mapping(uint => bool) flags; }
// Now we attach functions to the type.
// The attached functions can be used throughout the rest of the module.
// If you import the module, you have to
// repeat the using directive there, for example as
// import "flags.sol" as Flags;
// using {Flags.insert, Flags.remove, Flags.contains}
// for Flags.Data;
using {insert, remove, contains} for Data;
library Set {
function insert(Data storage self, uint value)
public
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function insert(Data storage self, uint value)
returns (bool)
{
if (self.flags[value])
return false; // already there
self.flags[value] = true;
return true;
}
function remove(Data storage self, uint value)
public
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function remove(Data storage self, uint value)
returns (bool)
{
if (!self.flags[value])
return false; // not there
self.flags[value] = false;
return true;
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{
return self.flags[value];
}
contract C {
using Set for Data; // this is the crucial change
Data knownValues;
function register(uint value) public {
@@ -82,12 +107,13 @@ 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 built-in types in that way.
In this example, we will use a library.
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.8 <0.9.0;
pragma solidity ^0.8.13;
library Search {
function indexOf(uint[] storage self, uint value)
@@ -100,22 +126,22 @@ It is also possible to extend elementary types in that way:
return type(uint).max;
}
}
using Search for uint[];
contract C {
using Search for uint[];
uint[] data;
function append(uint value) public {
data.push(value);
}
function replace(uint _old, uint _new) public {
function replace(uint from, uint to) public {
// This performs the library function call
uint index = data.indexOf(_old);
uint index = data.indexOf(from);
if (index == type(uint).max)
data.push(_new);
data.push(to);
else
data[index] = _new;
data[index] = to;
}
}
+1
View File
@@ -47,6 +47,7 @@ FixedBytes:
'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32';
For: 'for';
Function: 'function';
Global: 'global'; // not a real keyword
Hex: 'hex';
If: 'if';
Immutable: 'immutable';
+5 -4
View File
@@ -12,6 +12,7 @@ options { tokenVocab=SolidityLexer; }
sourceUnit: (
pragmaDirective
| importDirective
| usingDirective
| contractDefinition
| interfaceDefinition
| libraryDefinition
@@ -311,10 +312,10 @@ errorDefinition:
Semicolon;
/**
* Using directive to bind library functions to types.
* Can occur within contracts and libraries.
* Using directive to bind library functions and free functions to types.
* Can occur within contracts and libraries and at the file level.
*/
usingDirective: Using identifierPath For (Mul | typeName) Semicolon;
usingDirective: Using (identifierPath | (LBrace identifierPath (Comma identifierPath)* RBrace)) For (Mul | typeName) Global? Semicolon;
/**
* A type name can be an elementary type, a function type, a mapping type, a user-defined type
* (e.g. a contract or struct) or an array type.
@@ -388,7 +389,7 @@ inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack;
/**
* Besides regular non-keyword Identifiers, some keywords like 'from' and 'error' can also be used as identifiers.
*/
identifier: Identifier | From | Error | Revert;
identifier: Identifier | From | Error | Revert | Global;
literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral;
booleanLiteral: True | False;
+15 -16
View File
@@ -5,10 +5,8 @@ Solidity is an object-oriented, high-level language for implementing smart
contracts. Smart contracts are programs which govern the behaviour of accounts
within the Ethereum state.
Solidity is a `curly-bracket language <https://en.wikipedia.org/wiki/List_of_programming_languages_by_type#Curly-bracket_languages>`_.
It is influenced by C++, Python and JavaScript, and is designed to target the Ethereum Virtual Machine (EVM).
You can find more details about which languages Solidity has been inspired by in
the :doc:`language influences <language-influences>` section.
Solidity is a `curly-bracket language <https://en.wikipedia.org/wiki/List_of_programming_languages_by_type#Curly-bracket_languages>`_ designed to target the Ethereum Virtual Machine (EVM).
It is influenced by C++, Python and JavaScript. You can find more details about which languages Solidity has been inspired by in the :doc:`language influences <language-influences>` section.
Solidity is statically typed, supports inheritance, libraries and complex
user-defined types among other features.
@@ -90,24 +88,25 @@ our `Gitter channel <https://gitter.im/ethereum/solidity/>`_.
Translations
------------
Community volunteers help translate this documentation into several languages.
They have varying degrees of completeness and up-to-dateness. The English
Community contributors help translate this documentation into several languages.
Note that they have varying degrees of completeness and up-to-dateness. The English
version stands as a reference.
You can switch between languages by clicking on the flyout menu in the bottom-left corner
and selecting the preferred language.
* `French <https://docs.soliditylang.org/fr/latest/>`_
* `Indonesian <https://github.com/solidity-docs/id-indonesian>`_
* `Persian <https://github.com/solidity-docs/fa-persian>`_
* `Japanese <https://github.com/solidity-docs/ja-japanese>`_
* `Korean <https://github.com/solidity-docs/ko-korean>`_
* `Chinese <https://github.com/solidity-docs/zh-cn-chinese/>`_
.. note::
We recently set up a new GitHub organization and translation workflow to help streamline the
community efforts. Please refer to the `translation guide <https://github.com/solidity-docs/translation-guide>`_
for information on how to contribute to the community translations moving forward.
* `French <https://solidity-fr.readthedocs.io>`_ (in progress)
* `Italian <https://github.com/damianoazzolini/solidity>`_ (in progress)
* `Japanese <https://solidity-jp.readthedocs.io>`_
* `Korean <https://solidity-kr.readthedocs.io>`_ (in progress)
* `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated)
* `Simplified Chinese <https://learnblockchain.cn/docs/solidity/>`_ (in progress)
* `Spanish <https://solidity-es.readthedocs.io>`_
* `Turkish <https://github.com/denizozzgur/Solidity_TR/blob/master/README.md>`_ (partial)
for information on how to start a new language or contribute to the community translations.
Contents
========
+1 -2
View File
@@ -140,8 +140,7 @@ by checking if the lowest bit is set: short (not set) and long (set).
.. note::
Handling invalidly encoded slots is currently not supported but may be added in the future.
If you are compiling via the experimental IR-based compiler pipeline, reading an invalidly encoded
slot results in a ``Panic(0x22)`` error.
If you are compiling via IR, reading an invalidly encoded slot results in a ``Panic(0x22)`` error.
JSON Output
===========
+1 -1
View File
@@ -23,7 +23,7 @@ call completely.
Currently, the parameter ``--optimize`` activates the opcode-based optimizer for the
generated bytecode and the Yul optimizer for the Yul code generated internally, for example for ABI coder v2.
One can use ``solc --ir-optimized --optimize`` to produce an
optimized experimental Yul IR for a Solidity source. Similarly, one can use ``solc --strict-assembly --optimize``
optimized Yul IR for a Solidity source. Similarly, one can use ``solc --strict-assembly --optimize``
for a stand-alone Yul mode.
You can find more details on both optimizer modules and their optimization steps below.
+2 -2
View File
@@ -168,8 +168,8 @@ following:
.. code-block:: solidity
function balances(address _account) external view returns (uint) {
return balances[_account];
function balances(address account) external view returns (uint) {
return balances[account];
}
You can use this function to query the balance of a single account.
+60 -63
View File
@@ -15,11 +15,7 @@ The IR-based code generator was introduced with an aim to not only allow
code generation to be more transparent and auditable but also
to enable more powerful optimization passes that span across functions.
Currently, the IR-based code generator is still marked experimental,
but it supports all language features and has received a lot of testing,
so we consider it almost ready for production use.
You can enable it on the command line using ``--experimental-via-ir``
You can enable it on the command line using ``--via-ir``
or with the option ``{"viaIR": true}`` in standard-json and we
encourage everyone to try it out!
@@ -34,6 +30,48 @@ Semantic Only Changes
This section lists the changes that are semantic-only, thus potentially
hiding new and different behavior in existing code.
- The order of state variable initialization has changed in case of inheritance.
The order used to be:
- All state variables are zero-initialized at the beginning.
- Evaluate base constructor arguments from most derived to most base contract.
- Initialize all state variables in the whole inheritance hierarchy from most base to most derived.
- Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived.
New order:
- All state variables are zero-initialized at the beginning.
- Evaluate base constructor arguments from most derived to most base contract.
- For every contract in order from most base to most derived in the linearized hierarchy:
1. Initialize state variables.
2. Run the constructor (if present).
This causes differences in contracts where the initial value of a state
variable relies on the result of the constructor in another contract:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1;
contract A {
uint x;
constructor() {
x = 42;
}
function f() public view returns(uint256) {
return x;
}
}
contract B is A {
uint public y = f();
}
Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well.
With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42.
- When storage structs are deleted, every storage slot that contains
a member of the struct is set to zero entirely. Formerly, padding space
was left untouched.
@@ -78,8 +116,8 @@ hiding new and different behavior in existing code.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0;
contract C {
function f(uint _a) public pure mod() returns (uint _r) {
_r = _a++;
function f(uint a) public pure mod() returns (uint r) {
r = a++;
}
modifier mod() { _; _; }
}
@@ -116,47 +154,6 @@ hiding new and different behavior in existing code.
- New code generator: ``0`` as all parameters, including return parameters, will be re-initialized before
each ``_;`` evaluation.
- The order of contract initialization has changed in case of inheritance.
The order used to be:
- All state variables are zero-initialized at the beginning.
- Evaluate base constructor arguments from most derived to most base contract.
- Initialize all state variables in the whole inheritance hierarchy from most base to most derived.
- Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived.
New order:
- All state variables are zero-initialized at the beginning.
- Evaluate base constructor arguments from most derived to most base contract.
- For every contract in order from most base to most derived in the linearized hierarchy execute:
1. If present at declaration, initial values are assigned to state variables.
2. Constructor, if present.
This causes differences in some contracts, for example:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1;
contract A {
uint x;
constructor() {
x = 42;
}
function f() public view returns(uint256) {
return x;
}
}
contract B is A {
uint public y = f();
}
Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well.
With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42.
- Copying ``bytes`` arrays from memory to storage is implemented in a different way.
The old code generator always copies full words, while the new one cuts the byte
array after its end. The old behaviour can lead to dirty data being copied after
@@ -170,7 +167,7 @@ This causes differences in some contracts, for example:
contract C {
bytes x;
function f() public returns (uint _r) {
function f() public returns (uint r) {
bytes memory m = "tmp";
assembly {
mstore(m, 8)
@@ -178,7 +175,7 @@ This causes differences in some contracts, for example:
}
x = m;
assembly {
_r := sload(x.slot)
r := sload(x.slot)
}
}
}
@@ -201,8 +198,8 @@ This causes differences in some contracts, for example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1;
contract C {
function preincr_u8(uint8 _a) public pure returns (uint8) {
return ++_a + _a;
function preincr_u8(uint8 a) public pure returns (uint8) {
return ++a + a;
}
}
@@ -222,11 +219,11 @@ This causes differences in some contracts, for example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1;
contract C {
function add(uint8 _a, uint8 _b) public pure returns (uint8) {
return _a + _b;
function add(uint8 a, uint8 b) public pure returns (uint8) {
return a + b;
}
function g(uint8 _a, uint8 _b) public pure returns (uint8) {
return add(++_a + ++_b, _a + _b);
function g(uint8 a, uint8 b) public pure returns (uint8) {
return add(++a + ++b, a + b);
}
}
@@ -325,13 +322,13 @@ For example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1;
contract C {
function f(uint8 _a) public pure returns (uint _r1, uint _r2)
function f(uint8 a) public pure returns (uint r1, uint r2)
{
_a = ~_a;
a = ~a;
assembly {
_r1 := _a
r1 := a
}
_r2 = _a;
r2 = a;
}
}
@@ -340,6 +337,6 @@ The function ``f(1)`` returns the following values:
- Old code generator: (``fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe``, ``00000000000000000000000000000000000000000000000000000000000000fe``)
- New code generator: (``00000000000000000000000000000000000000000000000000000000000000fe``, ``00000000000000000000000000000000000000000000000000000000000000fe``)
Note that, unlike the new code generator, the old code generator does not perform a cleanup after the bit-not assignment (``_a = ~_a``).
This results in different values being assigned (within the inline assembly block) to return value ``_r1`` between the old and new code generators.
However, both code generators perform a cleanup before the new value of ``_a`` is assigned to ``_r2``.
Note that, unlike the new code generator, the old code generator does not perform a cleanup after the bit-not assignment (``a = ~a``).
This results in different values being assigned (within the inline assembly block) to return value ``r1`` between the old and new code generators.
However, both code generators perform a cleanup before the new value of ``a`` is assigned to ``r2``.
+2 -2
View File
@@ -3,8 +3,8 @@ Layout of a Solidity Source File
********************************
Source files can contain an arbitrary number of
:ref:`contract definitions<contract_structure>`, import_ directives,
:ref:`pragma directives<pragma>` and
:ref:`contract definitions<contract_structure>`, import_ ,
:ref:`pragma<pragma>` and :ref:`using for<using-for>` directives and
:ref:`struct<structs>`, :ref:`enum<enums>`, :ref:`function<functions>`, :ref:`error<errors>`
and :ref:`constant variable<constants>` definitions.
+4 -4
View File
@@ -210,9 +210,9 @@ using a second proxy:
contract ProxyWithMoreFunctionality {
PermissionlessProxy proxy;
function callOther(address _addr, bytes memory _payload) public
function callOther(address addr, bytes memory payload) public
returns (bool, bytes memory) {
return proxy.callOther(_addr, _payload);
return proxy.callOther(addr, payload);
}
// Other functions and other functionality
}
@@ -220,9 +220,9 @@ using a second proxy:
// This is the full contract, it has no other functionality and
// requires no privileges to work.
contract PermissionlessProxy {
function callOther(address _addr, bytes memory _payload) public
function callOther(address addr, bytes memory payload) public
returns (bool, bytes memory) {
return _addr.call(_payload);
return addr.call(payload);
}
}
+44 -44
View File
@@ -82,12 +82,12 @@ Overflow
uint immutable x;
uint immutable y;
function add(uint _x, uint _y) internal pure returns (uint) {
return _x + _y;
function add(uint x_, uint y_) internal pure returns (uint) {
return x_ + y_;
}
constructor(uint _x, uint _y) {
(x, y) = (_x, _y);
constructor(uint x_, uint y_) {
(x, y) = (x_, y_);
}
function stateAdd() public view returns (uint) {
@@ -116,7 +116,7 @@ Here, it reports the following:
Overflow.add(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) -- internal call
--> o.sol:9:20:
|
9 | return _x + _y;
9 | return x_ + y_;
| ^^^^^^^
If we add ``require`` statements that filter out overflow cases,
@@ -131,12 +131,12 @@ the SMTChecker proves that no overflow is reachable (by not reporting warnings):
uint immutable x;
uint immutable y;
function add(uint _x, uint _y) internal pure returns (uint) {
return _x + _y;
function add(uint x_, uint y_) internal pure returns (uint) {
return x_ + y_;
}
constructor(uint _x, uint _y) {
(x, y) = (_x, _y);
constructor(uint x_, uint y_) {
(x, y) = (x_, y_);
}
function stateAdd() public view returns (uint) {
@@ -155,7 +155,7 @@ An assertion represents an invariant in your code: a property that must be true
The code below defines a function ``f`` that guarantees no overflow.
Function ``inv`` defines the specification that ``f`` is monotonically increasing:
for every possible pair ``(_a, _b)``, if ``_b > _a`` then ``f(_b) > f(_a)``.
for every possible pair ``(a, b)``, if ``b > a`` then ``f(b) > f(a)``.
Since ``f`` is indeed monotonically increasing, the SMTChecker proves that our
property is correct. You are encouraged to play with the property and the function
definition to see what results come out!
@@ -166,14 +166,14 @@ definition to see what results come out!
pragma solidity >=0.8.0;
contract Monotonic {
function f(uint _x) internal pure returns (uint) {
require(_x < type(uint128).max);
return _x * 42;
function f(uint x) internal pure returns (uint) {
require(x < type(uint128).max);
return x * 42;
}
function inv(uint _a, uint _b) public pure {
require(_b > _a);
assert(f(_b) > f(_a));
function inv(uint a, uint b) public pure {
require(b > a);
assert(f(b) > f(a));
}
}
@@ -188,14 +188,14 @@ equal every element in the array.
pragma solidity >=0.8.0;
contract Max {
function max(uint[] memory _a) public pure returns (uint) {
function max(uint[] memory a) public pure returns (uint) {
uint m = 0;
for (uint i = 0; i < _a.length; ++i)
if (_a[i] > m)
m = _a[i];
for (uint i = 0; i < a.length; ++i)
if (a[i] > m)
m = a[i];
for (uint i = 0; i < _a.length; ++i)
assert(m >= _a[i]);
for (uint i = 0; i < a.length; ++i)
assert(m >= a[i]);
return m;
}
@@ -222,15 +222,15 @@ For example, changing the code to
pragma solidity >=0.8.0;
contract Max {
function max(uint[] memory _a) public pure returns (uint) {
require(_a.length >= 5);
function max(uint[] memory a) public pure returns (uint) {
require(a.length >= 5);
uint m = 0;
for (uint i = 0; i < _a.length; ++i)
if (_a[i] > m)
m = _a[i];
for (uint i = 0; i < a.length; ++i)
if (a[i] > m)
m = a[i];
for (uint i = 0; i < _a.length; ++i)
assert(m > _a[i]);
for (uint i = 0; i < a.length; ++i)
assert(m > a[i]);
return m;
}
@@ -243,7 +243,7 @@ gives us:
Warning: CHC: Assertion violation happens here.
Counterexample:
_a = [0, 0, 0, 0, 0]
a = [0, 0, 0, 0, 0]
= 0
Transaction trace:
@@ -251,7 +251,7 @@ gives us:
Test.max([0, 0, 0, 0, 0])
--> max.sol:14:4:
|
14 | assert(m > _a[i]);
14 | assert(m > a[i]);
State Properties
@@ -383,9 +383,9 @@ anything, including reenter the caller contract.
Unknown immutable unknown;
constructor(Unknown _u) {
require(address(_u) != address(0));
unknown = _u;
constructor(Unknown u) {
require(address(u) != address(0));
unknown = u;
}
modifier mutex {
@@ -395,8 +395,8 @@ anything, including reenter the caller contract.
lock = false;
}
function set(uint _x) mutex public {
x = _x;
function set(uint x_) mutex public {
x = x_;
}
function run() mutex public {
@@ -754,15 +754,15 @@ not mean loss of proving power.
{
function f(
bytes32 hash,
uint8 _v1, uint8 _v2,
bytes32 _r1, bytes32 _r2,
bytes32 _s1, bytes32 _s2
uint8 v1, uint8 v2,
bytes32 r1, bytes32 r2,
bytes32 s1, bytes32 s2
) public pure returns (address) {
address a1 = ecrecover(hash, _v1, _r1, _s1);
require(_v1 == _v2);
require(_r1 == _r2);
require(_s1 == _s2);
address a2 = ecrecover(hash, _v2, _r2, _s2);
address a1 = ecrecover(hash, v1, r1, s1);
require(v1 == v2);
require(r1 == r2);
require(s1 == s2);
address a2 = ecrecover(hash, v2, r2, s2);
assert(a1 == a2);
return a1;
}
+8 -8
View File
@@ -4,12 +4,12 @@
Mapping Types
=============
Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
of mapping type are declared using the syntax ``mapping(_KeyType => _ValueType) _VariableName``.
The ``_KeyType`` can be any
Mapping types use the syntax ``mapping(KeyType => ValueType)`` and variables
of mapping type are declared using the syntax ``mapping(KeyType => ValueType) VariableName``.
The ``KeyType`` can be any
built-in value type, ``bytes``, ``string``, or any contract or enum type. Other user-defined
or complex types, such as mappings, structs or array types are not allowed.
``_ValueType`` can be any type, including mappings, arrays and structs.
``ValueType`` can be any type, including mappings, arrays and structs.
You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised
such that every possible key exists and is mapped to a value whose
@@ -29,10 +29,10 @@ of contract functions that are publicly visible.
These restrictions are also true for arrays and structs that contain mappings.
You can mark state variables of mapping type as ``public`` and Solidity creates a
:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a parameter for the getter.
If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``.
If ``_ValueType`` is an array or a mapping, the getter has one parameter for
each ``_KeyType``, recursively.
:ref:`getter <visibility-and-getters>` for you. The ``KeyType`` becomes a parameter for the getter.
If ``ValueType`` is a value type or a struct, the getter returns ``ValueType``.
If ``ValueType`` is an array or a mapping, the getter has one parameter for
each ``KeyType``, recursively.
In the example below, the ``MappingExample`` contract defines a public ``balances``
mapping, with the key type an ``address``, and a value type a ``uint``, mapping
+17
View File
@@ -26,6 +26,23 @@ except for comparison operators where the result is always ``bool``.
The operators ``**`` (exponentiation), ``<<`` and ``>>`` use the type of the
left operand for the operation and the result.
Ternary Operator
----------------
The ternary operator is used in expressions of the form ``<expression> ? <trueExpression> : <falseExpression>``.
It evaluates one of the latter two given expressions depending upon the result of the evaluation of the main ``<expression>``.
If ``<expression>`` evaluates to ``true``, then ``<trueExpression>`` will be evaluated, otherwise ``<falseExpression>`` is evaluated.
The result of the ternary operator does not have a rational number type, even if all of its operands are rational number literals.
The result type is determined from the types of the two operands in the same way as above, converting to their mobile type first if required.
As a consequence, ``255 + (true ? 1 : 0)`` will revert due to arithmetic overflow.
The reason is that ``(true ? 1 : 0)`` is of ``uint8`` type, which forces the addition to be performed in ``uint8`` as well,
and 256 exceeds the range allowed for this type.
Another consequence is that an expression like ``1.5 + 1.5`` is valid but ``1.5 + (true ? 1.5 : 2.5)`` is not.
This is because the former is a rational expression evaluated in unlimited precision and only its final value matters.
The latter involves a conversion of a fractional rational number to an integer, which is currently disallowed.
.. index:: assignment, lvalue, ! compound operators
Compound and Increment/Decrement Operators
+8 -8
View File
@@ -511,21 +511,21 @@ Array slices are useful to ABI-decode secondary data passed in function paramete
/// @dev Address of the client contract managed by proxy i.e., this contract
address client;
constructor(address _client) {
client = _client;
constructor(address client_) {
client = client_;
}
/// Forward call to "setOwner(address)" that is implemented by client
/// after doing basic validation on the address argument.
function forward(bytes calldata _payload) external {
bytes4 sig = bytes4(_payload[:4]);
// Due to truncating behaviour, bytes4(_payload) performs identically.
// bytes4 sig = bytes4(_payload);
function forward(bytes calldata payload) external {
bytes4 sig = bytes4(payload[:4]);
// Due to truncating behaviour, bytes4(payload) performs identically.
// bytes4 sig = bytes4(payload);
if (sig == bytes4(keccak256("setOwner(address)"))) {
address owner = abi.decode(_payload[4:], (address));
address owner = abi.decode(payload[4:], (address));
require(owner != address(0), "Address of owner cannot be zero.");
}
(bool status,) = client.delegatecall(_payload);
(bool status,) = client.delegatecall(payload);
require(status, "Forwarded call failed.");
}
}
+10 -1
View File
@@ -463,7 +463,7 @@ There is no additional semantic meaning added to a number literal containing und
the underscores are ignored.
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
using them together with a non-literal expression or by explicit conversion).
using them together with anything else than a number literal expression (like boolean literals) or by explicit conversion).
This means that computations do not overflow and divisions do not truncate
in number literal expressions.
@@ -471,6 +471,15 @@ For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``
although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results
in the integer ``4`` (although non-integers were used in between).
.. warning::
While most operators produce a literal expression when applied to literals, there are certain operators that do not follow this pattern:
- Ternary operator (``... ? ... : ...``),
- Array subscript (``<array>[<index>]``).
You might expect expressions like ``255 + (true ? 1 : 0)`` or ``255 + [1, 2, 3][0]`` to be equivalent to using the literal 256
directly, but in fact they are computed within the type ``uint8`` and can overflow.
Any operator that can be applied to integers can also be applied to number literal expressions as
long as the operands are integers. If any of the two is fractional, bit operations are disallowed
and exponentiation is disallowed if the exponent is fractional (because that might result in
+1 -1
View File
@@ -297,7 +297,7 @@ Input Description
// tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul or berlin
"evmVersion": "byzantium",
// Optional: Change compilation pipeline to go through the Yul intermediate representation.
// This is a highly EXPERIMENTAL feature, not to be used for production. This is false by default.
// This is false by default.
"viaIR": true,
// Optional: Debugging settings
"debug": {
+9 -4
View File
@@ -1124,6 +1124,11 @@ regular strings in native encoding. For code,
Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter.
.. note::
An object with a name that ends in ``_deployed`` is treated as deployed code by the Yul optimizer.
The only consequence of this is a different gas cost heuristic in the optimizer.
.. note::
Data objects or sub-objects whose names contain a ``.`` can be defined
@@ -1172,17 +1177,17 @@ An example Yul Object is shown below:
// now return the runtime object (the currently
// executing code is the constructor code)
size := datasize("runtime")
size := datasize("Contract1_deployed")
offset := allocate(size)
// This will turn into a memory->memory copy for Ewasm and
// a codecopy for EVM
datacopy(offset, dataoffset("runtime"), size)
datacopy(offset, dataoffset("Contract1_deployed"), size)
return(offset, size)
}
data "Table2" hex"4123"
object "runtime" {
object "Contract1_deployed" {
code {
function allocate(size) -> ptr {
ptr := mload(0x40)
@@ -1204,7 +1209,7 @@ An example Yul Object is shown below:
// code here ...
}
object "runtime" {
object "Contract2_deployed" {
code {
// code here ...
}