diff --git a/Changelog.md b/Changelog.md index fc61a1b19..d13bc1332 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Language Features: * Inheritance: A function that overrides only a single interface function does not require the ``override`` specifier. * Type System: Support ``type().min`` and ``type().max`` for enums. + * User Defined Value Type: allows creating a zero cost abstraction over a value type with stricter type requirements. Compiler Features: diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 1b61351fa..6f50c70d9 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -113,6 +113,9 @@ them. +-------------------------------+-----------------------------------------------------------------------------+ |:ref:`enum` |``uint8`` | +-------------------------------+-----------------------------------------------------------------------------+ +|:ref:`user defined value types |its underlying value type | +|` | | ++-------------------------------+-----------------------------------------------------------------------------+ |:ref:`struct` |``tuple`` | +-------------------------------+-----------------------------------------------------------------------------+ diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index e3d6ed0d7..c1ac3d264 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -628,6 +628,69 @@ smallest and respectively largest value of the given enum. .. note:: Enums can also be declared on the file level, outside of contract or library definitions. +.. index:: ! user defined value type, custom type + +.. _user-defined-value-types: + +User Defined Value Types +------------------------ + +A user defined value type allows creating a zero cost abstraction over an elementary value type. +This is similar to an alias, but with stricter type requirements. + +A user defined value type is defined using ``type C is V``, where ``C`` is the name of the newly +introduced type and ``V`` has to be a built-in value type (the "underlying type"). The function +``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the +function ``C.unwrap`` is used to convert from the custom type to the underlying type. + +The type ``C`` does not have any operators or bound member functions. In particular, even the +operator ``==`` is not defined. Explicit and implicit conversions to and from other types are +disallowed. + +The data-representation of values of such types are inherited from the underlying type +and the underlying type is also used in the ABI. + +The following example illustrates a custom type ``UFixed256x18`` representing a decimal fixed point +type with 18 decimals and a minimal library to do arithmetic operations on the type. + + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.8; + + // Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. + type UFixed256x18 is uint256; + + /// A minimal library to do fixed point operations on UFixed256x18. + library FixedMath { + uint constant multiplier = 10**18; + + /// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked + /// arithmetic on uint256. + function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b)); + } + /// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked + /// arithmetic on uint256. + function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b); + } + /// Truncates UFixed256x18 to the nearest uint256 number. + function truncate(UFixed256x18 a) internal pure returns (uint256) { + return UFixed256x18.unwrap(a) / multiplier; + } + /// Turns a uint256 into a UFixed256x18 of the same value. + /// Reverts if the integer is too large. + function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(a * multiplier); + } + } + +Notice how ``UFixed256x18.wrap`` and ``FixedMath.toUFixed256x18`` have the same signature but +perform two very different operations: The ``UFixed256x18.wrap`` function returns a ``UFixed256x18`` +that has the same data representation as the input, whereas ``toUFixed256x18`` returns a +``UFixed256x18`` that has the same numerical value. .. index:: ! function type, ! type; function