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) | ||||
| 
 | ||||
| Language Features: | ||||
| * Allow defining custom operators for user-defined value types via ``using {f as +} for T global`` syntax. | ||||
| 
 | ||||
| 
 | ||||
| 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. | ||||
| 
 | ||||
| 
 | ||||
| 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) | ||||
| 
 | ||||
| Language Features: | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| .. index:: ! using for, library | ||||
| .. index:: ! using for, library, ! operator; user-defined | ||||
| 
 | ||||
| .. _using-for: | ||||
| 
 | ||||
| @ -6,37 +6,87 @@ | ||||
| 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 | ||||
| The directive ``using A for B`` can be used to attach | ||||
| 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. | ||||
| 
 | ||||
| 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;``) - | ||||
|   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. | ||||
| - A list of functions, optionally with an operator name assigned (e.g. | ||||
|   ``using {f, g as +, h, L.t} for uint``). | ||||
|   If no operator is specified, the function can be either a library function or a free function and | ||||
|   is attached to the type as a member function. | ||||
|   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). | ||||
| 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`` | ||||
| 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 | ||||
| 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 (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 | ||||
| first parameter of each of these functions. This check is | ||||
| 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 | ||||
| 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 | ||||
| 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 +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 | ||||
| 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.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`. | ||||
|  * 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 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 | ||||
|  * 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