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
c3d3285921
commit
c583f56db7
@ -2,6 +2,7 @@
|
||||
|
||||
Language Features:
|
||||
* Allow named parameters in mapping types.
|
||||
* Allow defining custom operators for user-defined value types via ``using {f as +} for Typename;`` syntax.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
@ -36,6 +37,10 @@ Bugfixes:
|
||||
* TypeChecker: Fix bug where private library functions could be attached with ``using for`` outside of their declaration scope.
|
||||
|
||||
|
||||
AST Changes:
|
||||
* AST: Add ``function`` field to ``UnaryOperation`` and ``BinaryOperation`` AST nodes and ``functionList.operator`` field to ``UsingForDirective`` AST nodes.
|
||||
|
||||
|
||||
### 0.8.17 (2022-09-08)
|
||||
|
||||
Important Bugfixes:
|
||||
|
@ -7,9 +7,11 @@ Using For
|
||||
*********
|
||||
|
||||
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
|
||||
functions (``A``) as operators to user-defined value types
|
||||
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).
|
||||
The operator functions receive operands as parameters.
|
||||
|
||||
It is valid either at file level or inside a contract,
|
||||
at contract level.
|
||||
@ -20,7 +22,15 @@ The first part, ``A``, can be one of:
|
||||
only those functions will be attached to the type as member functions.
|
||||
Note that private library functions can only be specified when ``using for`` is inside the library.
|
||||
- The name of a library (e.g. ``using L for uint;``) -
|
||||
all non-private functions of the library are attached to the type.
|
||||
all non-private functions of the library are attached to the type
|
||||
as member functions
|
||||
- a list of assignments of file-level or public/internal/private library functions to operators
|
||||
(e.g. ``using {f as +, g as -} for T;``) - the functions will be attached to the type (``T``)
|
||||
as operators. The following binary operators are allowed to be used on the list: ``|``,
|
||||
``^``, ``&``, ``+``, ``-``, ``*``, ``/``, ``%``, ``==``, ``!=``, ``<``, ``>``, ``<=``,
|
||||
``>=``, ``<<``, ``>>``, ``**``. Allowed unary operators are: ``~``, ``!``, ``-``.
|
||||
If an operator can be both binary and unary, it is allowed to have each variant specified
|
||||
on the list (e.g. ``using {sub as -, unsub as -} for T``).
|
||||
|
||||
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 *;``),
|
||||
@ -38,6 +48,13 @@ 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.
|
||||
|
||||
If you define an operator for a user-defined type (``using {f as +} for T``), then
|
||||
the type (``T``), types of function parameters and the type of the function return value
|
||||
have to be the same. The type (``T``) does not include data location.
|
||||
But, data location of the function parameters and function return value must be
|
||||
the same. There is an exception for comparison operators for which, the return value
|
||||
type is always ``bool``.
|
||||
|
||||
The ``using A for B;`` directive is active only within the current
|
||||
scope (either the contract or the current module/source unit),
|
||||
including within all of its functions, and has no effect
|
||||
@ -46,7 +63,7 @@ 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
|
||||
effect that the functions and operators are attached to the type everywhere
|
||||
the type is available (including other files), not only in the
|
||||
scope of the using statement.
|
||||
|
||||
@ -150,3 +167,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
|
||||
is when storage reference variables are used or when internal library
|
||||
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.18;
|
||||
|
||||
type UFixed16x2 is uint16;
|
||||
|
||||
using {
|
||||
add as +,
|
||||
div as /
|
||||
} for UFixed16x2;
|
||||
|
||||
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,15 @@ private:
|
||||
* 1. `using LibraryName for T` attaches all functions from the library `LibraryName` to the type `T`.
|
||||
* 2. `using LibraryName for *` attaches to all types.
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* For version 4, T has to be user-defined value type. All parameters and
|
||||
* return value of all the functions have to be of type T.
|
||||
*
|
||||
* 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
|
||||
* attached to all objects of that type regardless of scope.
|
||||
|
Loading…
Reference in New Issue
Block a user