mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined operators: Documentation
This commit is contained in:
parent
1a83fa7ebc
commit
2e8d50eca2
@ -1,6 +1,7 @@
|
|||||||
### 0.8.19 (unreleased)
|
### 0.8.19 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
* Allow defining custom operators for user-defined value types via ``using {f as +} for T global`` syntax.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
@ -16,6 +17,10 @@ Bugfixes:
|
|||||||
* SMTChecker: Fix internal error caused by unhandled ``z3`` expressions that come from the solver when bitwise operators are used.
|
* SMTChecker: Fix internal error caused by unhandled ``z3`` expressions that come from the solver when bitwise operators are used.
|
||||||
|
|
||||||
|
|
||||||
|
AST Changes:
|
||||||
|
* AST: Add ``function`` field to ``UnaryOperation`` and ``BinaryOperation`` AST nodes. ``functionList`` in ``UsingForDirective`` AST nodes will now contain ``operator`` and ``definition`` members instead of ``function`` when the list entry defines an operator.
|
||||||
|
|
||||||
|
|
||||||
### 0.8.18 (2023-02-01)
|
### 0.8.18 (2023-02-01)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.. index:: ! using for, library
|
.. index:: ! using for, library, ! operator; user-defined
|
||||||
|
|
||||||
.. _using-for:
|
.. _using-for:
|
||||||
|
|
||||||
@ -6,37 +6,87 @@
|
|||||||
Using For
|
Using For
|
||||||
*********
|
*********
|
||||||
|
|
||||||
The directive ``using A for B;`` can be used to attach
|
The directive ``using A for B`` can be used to attach
|
||||||
functions (``A``) as member functions to any type (``B``).
|
functions (``A``) as operators to user-defined value types
|
||||||
These functions will receive the object they are called on
|
or as member functions to any type (``B``).
|
||||||
|
The member functions receive the object they are called on
|
||||||
as their first parameter (like the ``self`` variable in Python).
|
as their first parameter (like the ``self`` variable in Python).
|
||||||
|
The operator functions receive operands as parameters.
|
||||||
|
|
||||||
It is valid either at file level or inside a contract,
|
It is valid either at file level or inside a contract,
|
||||||
at contract level.
|
at contract level.
|
||||||
|
|
||||||
The first part, ``A``, can be one of:
|
The first part, ``A``, can be one of:
|
||||||
|
|
||||||
- A list of file-level or library functions (e.g. ``using {f, g, h, L.t} for uint;``) -
|
- A list of functions, optionally with an operator name assigned (e.g.
|
||||||
only those functions will be attached to the type as member functions.
|
``using {f, g as +, h, L.t} for uint``).
|
||||||
Note that private library functions can only be specified when ``using for`` is inside the library.
|
If no operator is specified, the function can be either a library function or a free function and
|
||||||
- The name of a library (e.g. ``using L for uint;``) -
|
is attached to the type as a member function.
|
||||||
all non-private functions of the library are attached to the type.
|
Otherwise it must be a free function and it becomes the definition of that operator on the type.
|
||||||
|
- The name of a library (e.g. ``using L for uint``) -
|
||||||
|
all non-private functions of the library are attached to the type
|
||||||
|
as member functions
|
||||||
|
|
||||||
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
|
At file level, the second part, ``B``, has to be an explicit type (without data location specifier).
|
||||||
Inside contracts, you can also use ``*`` in place of the type (e.g. ``using L for *;``),
|
Inside contracts, you can also use ``*`` in place of the type (e.g. ``using L for *;``),
|
||||||
which has the effect that all functions of the library ``L``
|
which has the effect that all functions of the library ``L``
|
||||||
are attached to *all* types.
|
are attached to *all* types.
|
||||||
|
|
||||||
If you specify a library, *all* functions in the library get attached,
|
If you specify a library, *all* non-private functions in the library get attached,
|
||||||
even those where the type of the first parameter does not
|
even those where the type of the first parameter does not
|
||||||
match the type of the object. The type is checked at the
|
match the type of the object. The type is checked at the
|
||||||
point the function is called and function overload
|
point the function is called and function overload
|
||||||
resolution is performed.
|
resolution is performed.
|
||||||
|
|
||||||
If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint;``),
|
If you use a list of functions (e.g. ``using {f, g, h, L.t} for uint``),
|
||||||
then the type (``uint``) has to be implicitly convertible to the
|
then the type (``uint``) has to be implicitly convertible to the
|
||||||
first parameter of each of these functions. This check is
|
first parameter of each of these functions. This check is
|
||||||
performed even if none of these functions are called.
|
performed even if none of these functions are called.
|
||||||
|
Note that private library functions can only be specified when ``using for`` is inside a library.
|
||||||
|
|
||||||
|
If you define an operator (e.g. ``using {f as +} for T``), then the type (``T``) must be a
|
||||||
|
:ref:`user-defined value type <user-defined-value-types>` and the definition must be a ``pure`` function.
|
||||||
|
Operator definitions must be global.
|
||||||
|
The following operators can be defined this way:
|
||||||
|
|
||||||
|
+------------+----------+---------------------------------------------+
|
||||||
|
| Category | Operator | Possible signatures |
|
||||||
|
+============+==========+=============================================+
|
||||||
|
| Bitwise | ``&`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``|`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``^`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``~`` | ``function (T) pure returns (T)`` |
|
||||||
|
+------------+----------+---------------------------------------------+
|
||||||
|
| Arithmetic | ``+`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``-`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| + +---------------------------------------------+
|
||||||
|
| | | ``function (T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``*`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``/`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``%`` | ``function (T, T) pure returns (T)`` |
|
||||||
|
+------------+----------+---------------------------------------------+
|
||||||
|
| Comparison | ``==`` | ``function (T, T) pure returns (bool)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``!=`` | ``function (T, T) pure returns (bool)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``<`` | ``function (T, T) pure returns (bool)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``<=`` | ``function (T, T) pure returns (bool)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``>`` | ``function (T, T) pure returns (bool)`` |
|
||||||
|
| +----------+---------------------------------------------+
|
||||||
|
| | ``>=`` | ``function (T, T) pure returns (bool)`` |
|
||||||
|
+------------+----------+---------------------------------------------+
|
||||||
|
|
||||||
|
Note that unary and binary ``-`` need separate definitions.
|
||||||
|
The compiler will choose the right definition based on how the operator is invoked.
|
||||||
|
|
||||||
The ``using A for B;`` directive is active only within the current
|
The ``using A for B;`` directive is active only within the current
|
||||||
scope (either the contract or the current module/source unit),
|
scope (either the contract or the current module/source unit),
|
||||||
@ -46,7 +96,7 @@ outside of the contract or module in which it is used.
|
|||||||
When the directive is used at file level and applied to a
|
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,
|
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
|
the word ``global`` can be added at the end. This will have the
|
||||||
effect that the functions are attached to the type everywhere
|
effect that the functions and operators are attached to the type everywhere
|
||||||
the type is available (including other files), not only in the
|
the type is available (including other files), not only in the
|
||||||
scope of the using statement.
|
scope of the using statement.
|
||||||
|
|
||||||
@ -150,3 +200,37 @@ if you pass memory or value types, a copy will be performed, even in case of the
|
|||||||
``self`` variable. The only situation where no copy will be performed
|
``self`` variable. The only situation where no copy will be performed
|
||||||
is when storage reference variables are used or when internal library
|
is when storage reference variables are used or when internal library
|
||||||
functions are called.
|
functions are called.
|
||||||
|
|
||||||
|
Another example shows how to define a custom operator for a user-defined type:
|
||||||
|
|
||||||
|
.. code-block:: solidity
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
pragma solidity ^0.8.19;
|
||||||
|
|
||||||
|
type UFixed16x2 is uint16;
|
||||||
|
|
||||||
|
using {
|
||||||
|
add as +,
|
||||||
|
div as /
|
||||||
|
} for UFixed16x2 global;
|
||||||
|
|
||||||
|
uint32 constant SCALE = 100;
|
||||||
|
|
||||||
|
function add(UFixed16x2 a, UFixed16x2 b) pure returns (UFixed16x2) {
|
||||||
|
return UFixed16x2.wrap(UFixed16x2.unwrap(a) + UFixed16x2.unwrap(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
function div(UFixed16x2 a, UFixed16x2 b) pure returns (UFixed16x2) {
|
||||||
|
uint32 a32 = UFixed16x2.unwrap(a);
|
||||||
|
uint32 b32 = UFixed16x2.unwrap(b);
|
||||||
|
uint32 result32 = a32 * SCALE / b32;
|
||||||
|
require(result32 <= type(uint16).max, "Divide overflow");
|
||||||
|
return UFixed16x2.wrap(uint16(a32 * SCALE / b32));
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Math {
|
||||||
|
function avg(UFixed16x2 a, UFixed16x2 b) public pure returns (UFixed16x2) {
|
||||||
|
return (a + b) / UFixed16x2.wrap(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -647,11 +647,17 @@ private:
|
|||||||
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`.
|
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`.
|
||||||
* 2. `using LibraryName for *` attaches to all types.
|
* 2. `using LibraryName for *` attaches to all types.
|
||||||
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ..., `fn`, respectively to `T`.
|
* 3. `using {f1, f2, ..., fn} for T` attaches the functions `f1`, `f2`, ..., `fn`, respectively to `T`.
|
||||||
|
* 4. `using {f1 as op1, f2 as op2, ..., fn as opn} for T` implements operator `opn` for type `T` with function `fn`.
|
||||||
*
|
*
|
||||||
* For version 3, T has to be implicitly convertible to the first parameter type of
|
* For version 3, T has to be implicitly convertible to the first parameter type of
|
||||||
* all functions, and this is checked at the point of the using statement. For versions 1 and
|
* all functions, and this is checked at the point of the using statement. For versions 1 and
|
||||||
* 2, this check is only done when a function is called.
|
* 2, this check is only done when a function is called.
|
||||||
*
|
*
|
||||||
|
* For version 4, T has to be user-defined value type and the function must be pure.
|
||||||
|
* All parameters and return value of all the functions have to be of type T.
|
||||||
|
* This version can be combined with version 3 - a single directive may attach functions to the
|
||||||
|
* type and define operators on it at the same time.
|
||||||
|
*
|
||||||
* Finally, `using {f1, f2, ..., fn} for T global` is also valid at file level, as long as T is
|
* Finally, `using {f1, f2, ..., fn} for T global` is also valid at file level, as long as T is
|
||||||
* a user-defined type defined in the same file at file level. In this case, the methods are
|
* a user-defined type defined in the same file at file level. In this case, the methods are
|
||||||
* attached to all objects of that type regardless of scope.
|
* attached to all objects of that type regardless of scope.
|
||||||
|
Loading…
Reference in New Issue
Block a user