From edcc3af9c8dfeb6f2721bd2e634ff8ba41201593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 13 Apr 2023 21:36:48 +0200 Subject: [PATCH] docs: Literal suffixes --- docs/cheatsheet.rst | 4 +- docs/contracts/functions.rst | 4 + docs/control-structures.rst | 36 ++++ docs/types.rst | 2 + docs/types/literal-suffixes.rst | 269 ++++++++++++++++++++++++++++ docs/types/value-types.rst | 10 +- docs/units-and-global-variables.rst | 3 +- 7 files changed, 324 insertions(+), 4 deletions(-) create mode 100644 docs/types/literal-suffixes.rst diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 08494ecc6..756ac17b0 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -141,7 +141,7 @@ Function Visibility Specifiers - ``internal``: only visible internally -.. index:: modifiers, pure, view, payable, constant, anonymous, indexed +.. index:: modifiers, pure, view, payable, constant, anonymous, indexed, literal;suffix Modifiers ========= @@ -157,4 +157,4 @@ Modifiers behaviour to be changed in derived contracts. - ``override``: States that this function, modifier or public state variable changes the behaviour of a function or modifier in a base contract. - +- ``suffix`` for free functions: Designates the function as a :ref:`literal suffix` definition. diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index ba903dce1..95f475ff9 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -41,6 +41,10 @@ that call them, similar to internal library functions. is that free functions do not have direct access to the variable ``this``, storage variables and functions not in their scope. +There is a special category of free functions called :ref:`literal suffixes`. +Such functions are marked with the ``suffix`` modifier, and can be used to define convenient +conversions from literals to arbitrary types. + .. _function-parameters-return-variables: Function Parameters and Return Variables diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 88fb53a56..525ae5113 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -195,6 +195,42 @@ can still return a value to the caller by use of the ``return`` statement. } } +.. index:: ! literal suffix; suffix call syntax + +Suffix Calls +------------ + +For free functions designated as :ref:`literal suffix` definitions there is +another way to perform a function call when the argument is a literal. +The functions from the example below can be used as suffixes: + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.21; + + type Integer is int; + type Float is uint; + + function i(int value) pure suffix returns (Integer) { + return Integer.wrap(value); + } + + function f(uint128 mantissa, uint128 exponent) pure suffix returns (Float) { + return Float.wrap((mantissa << 128) | exponent); + } + +``42 i`` and ``4.2 f`` are valid ways to call the functions defined above, equivalent to ``i(42)`` +and ``f(42, 1)``, respectively. + +For single-parameter suffixes, this call syntax is equivalent to passing the literal as an argument +to an internal function call. +For two-parameter suffixes, :ref:`fractional decomposition` is performed +first in order to obtain the two input values. + +See the section on :ref:`calling suffix functions` for more information +on the limitations of this call syntax. + .. index:: ! new, contracts;creating diff --git a/docs/types.rst b/docs/types.rst index d929a274f..5db5886f1 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -26,4 +26,6 @@ tuple with a second ``bool`` value denoting success. .. include:: types/operators.rst +.. include:: types/literal-suffixes.rst + .. include:: types/conversion.rst diff --git a/docs/types/literal-suffixes.rst b/docs/types/literal-suffixes.rst new file mode 100644 index 000000000..ddb14b9ce --- /dev/null +++ b/docs/types/literal-suffixes.rst @@ -0,0 +1,269 @@ +.. index:: ! literal suffix +.. _literal_suffixes: + +Literal Suffixes +================ + +While Solidity :ref:`provides implicit conversions` from its literals to selected types, +there are types for which no built-in conversions are available (most notably the +:ref:`user-defined value types`). + +To fill this gap, it is possible to define custom conversions as *literal suffixes*. + +.. code-block:: solidity + :force: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.21; + + type Coin is uint; + using {add as +, eq as ==} for Coin global; + + function add(Coin a, Coin b) pure returns (Coin) { + return Coin.wrap(Coin.unwrap(a) + Coin.unwrap(b)); + } + + function eq(Coin a, Coin b) pure returns (bool) { + return Coin.unwrap(a) == Coin.unwrap(b); + } + + function c(uint mantissa, uint exponent) pure suffix returns (Coin) { + return Coin.wrap(mantissa * 10**6 / 10**exponent); + } + + function uc(uint microValue) pure suffix returns (Coin) { + return Coin.wrap(microValue); + } + + contract CoinSeller { + mapping(address => Coin) balances; + + function buy() public payable { + require(msg.value == 1 ether); + assert(1.5 c == 1_500_000 uc); + balances[msg.sender] = balances[msg.sender] + 1.5 c; + } + } + +.. index:: function;call +.. _calling_suffix_functions: + +Calling Suffix Functions +------------------------ + +There are two ways to call a suffix function: a *suffix call* and a *function call*. + +.. index:: ! literal suffix; suffix call syntax + +Suffix Call Syntax +^^^^^^^^^^^^^^^^^^ + +A suffix call has the same syntax as a literal with a :ref:`denomination`: + +.. code-block:: solidity + + 42 suffix; + 1.23 suffix; + 0x1234 suffix; + 'abc' suffix; + hex"12ff" suffix; + unicode"😃" suffix; + true suffix; + +The literal passed as input to the suffix function must be immediately followed by the name of the suffix. +The two must be separated by whitespace unless it is a string, unicode or hexadecimal string literal, +in which case the whitespace is optional (i.e. ``'abc'suffix`` is also allowed). + +This call syntax supports only a single literal argument. +Variables or expressions (even as simple as wrapping the literal in parentheses) are not allowed. +:ref:`Suffix functions defined with two parameters` are also invoked with +one literal - the :ref:`decomposition` of the literal into two values is +performed implicitly by the compiler. + +.. warning:: + There are no negative number literals in Solidity. + A literal with a minus sign is an expression. + ``-123 suffix`` is equivalent to ``-(123 suffix)``, so ``suffix`` does not receive ``-123`` as input. + The argument is instead ``123``, and the negation is applied to the returned value. + +.. note:: + :ref:`String literal concatenation` produces a single literal at + compilation time and therefore is not treated as an expression. + This means that e.g., ``"abc" "def" suffix`` is a valid suffix call. + +.. note:: + Fractional decomposition is performed at compilation time. + Using the suffix call syntax does not incur any extra gas cost compared to the equivalent + function call. + +.. index:: ! literal suffix; function call syntax, overload +.. _calling_suffix_functions_with_function_call_syntax: + +Function Call Syntax +^^^^^^^^^^^^^^^^^^^^ + +Suffix definitions are in all respects valid free functions, and this includes the ability to call +them directly: + +.. code-block:: solidity + + suffix(42); + suffix(123, 2); + suffix(0x1234); + suffix('abc'); + suffix(hex"12ff"); + suffix(unicode"😃"); + suffix(true); + +This also makes it possible to call such functions with arguments which are not literals. + +Note that the fractional decomposition is not performed for this kind of call - +:ref:`two-parameter suffix functions` must be explicitly called with +two arguments. + +Regardless of the call syntax used and in contrast to applying a denomination, the result of the +call is itself not considered a literal. +As a consequence, it cannot be used as input of another suffix call, and calculations on it are performed +within its type rather than in arbitrary precision (as is the case with calculations on rational number +literals): + +.. code-block:: solidity + + 123 suffix1 suffix2; // This will not compile. + suffix1(123) suffix2; // This will not compile. + +Such calls are possible only with the function call syntax: + +.. code-block:: solidity + + suffix2(123 suffix1); // This is fine. + suffix2(suffix1(123)); // This is fine. + +.. note:: + As all free functions, suffix definitions can be :ref:`overloaded`. + Overloaded suffixes, however, cannot be invoked using the suffix call syntax. + +.. index:: ! literal suffix;definition, function;free + +Defining Suffix Functions +------------------------- + +Literal suffixes can be defined by applying the built-in ``suffix`` modifier to a :ref:`free function`. + +Only pure functions can be used as suffixes. +This means that suffixes cannot read or modify blockchain state. +As with all pure functions, however, they can perform pure external calls. + +.. index:: literal;address +.. _suffix_function_parameters: + +Suffix Function Parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A suffix function must accept and return exactly one value. +As a special case, suffixes on :ref:`rational literals` can optionally accept two arguments, +produced by the :ref:`fractional decomposition` of such a literal. + +Suffixes can only have parameters of types for which an implicit conversion from a literal exists. +For single-parameter suffixes, this includes the following types: + ++-------------------------------------------------------------+----------------------------------------------------------------+ +| Parameter type | Accepted literals | ++=============================================================+================================================================+ +| ``bool`` | - :ref:`Boolean` literals | ++-------------------------------------------------------------+----------------------------------------------------------------+ +| ``uint8``, ..., ``uint256``, ``int8``, ..., ``int256`` | - :ref:`Rational literals` (including zero) | +| | - Hexadecimal number literals | ++-------------------------------------------------------------+----------------------------------------------------------------+ +| ``address`` | - :ref:`Address literals` | ++-------------------------------------------------------------+----------------------------------------------------------------+ +| ``bytes1``, ..., ``bytes32`` | - Hexadecimal number literals (not for ``bytes20``) | +| | - :ref:`Hexadecimal string literals` | +| | - :ref:`String literals` | +| | - :ref:`Unicode literals` | +| | - :ref:`Zero` | ++-------------------------------------------------------------+----------------------------------------------------------------+ +| ``bytes`` | - :ref:`Hexadecimal string literals` | +| | - :ref:`String literals` | +| | - :ref:`Unicode literals` | ++-------------------------------------------------------------+----------------------------------------------------------------+ +| ``string`` | - :ref:`String literals` | +| | - :ref:`Unicode literals` | ++-------------------------------------------------------------+----------------------------------------------------------------+ + +For two-parameter suffix functions, the first parameter (representing the mantissa) can be of any integer type. +The second parameter (the exponent) must be of an unsigned integer type. + +.. note:: + :ref:`The function call syntax` is the only + way to pass a negative integer value into a suffix function. + Despite this, signed integer types are allowed for suffix parameters. + They are still useful in cases where it is desirable to limit the range of the parameter or to + avoid explicit conversions when the return type is signed. + +.. note:: + 40-digit literals prefixed with ``0x`` such as, for example, ``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` + always represent ``address`` literals in the language. + To invoke a suffix accepting ``bytes20`` you must use one of the other literal kinds implicitly + convertible to ``bytes20``, e.g., a hexadecimal string literal + (``hex"dCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF"``). + +.. note:: + Suffix functions accepting ``address payable`` are not allowed since address literals are never payable. + +Suffix functions may not accept or return reference types with ``storage`` or ``calldata`` locations. + +.. index:: ! fractional decomposition +.. _fractional_decomposition: + +Fractional Decomposition +------------------------ + +To allow defining suffixes that work with fractional literals, like ``1.23``, the language allows +a special form of a suffix definition. +Such a suffix can be considered a more general form of a suffix taking a single integer argument. + +A single-parameter suffix can be applied only to those rational number literals which represent +integers. +Let us consider the following suffix definition: + +.. code-block:: solidity + + function kg(uint grams) pure suffix returns (uint) { + return 1000 * grams; + } + +The ``kg`` suffix can receive integer values like ``123 kg``, ``1.23e2 kg``, or ``12300e-2 kg``. +However, invoking such a suffix with a fractional number (e.g., ``1.23 kg``) triggers an error. +We can fix that by adding an *exponent* parameter: + +.. code-block:: solidity + + function kg(uint mantissa, uint exponent) pure suffix returns (uint) { + return 1000 * mantissa / 10**exponent; + } + +When defined this way, the suffix can handle all the literals it could previously, while ``1.23 kg`` +also becomes a valid expression, equivalent to ``kg(123, 2)``. + +More generally, the argument of such a suffix call is decomposed into two integer values (``mantissa`` +and ``exponent``), such that: + +#. ``mantissa * 10**-exponent`` is equal to the value of the literal. +#. ``exponent`` is the smallest possible non-negative integer value satisfying the equation. + +The two rules provide unambiguous decomposition in all cases. +For example: + +- ``123000`` is decomposed into ``123000 * 10**-0`` (i.e. ``123000`` for ``mantissa`` and ``0`` for ``exponent``). + Not ``123 * 10**3`` or ``123000000 * 10**-3``. + + In general, when the suffix is invoked on an integer, ``mantissa`` is always equal to that integer + and ``exponent`` is ``0``. +- ``1.23`` is decomposed into ``123 * 10**-2``, not ``1.23 * 10**-0`` or ``123000 * 10**-5``. + + In general, when the suffix is invoked on a fractional number, ``exponent`` is the exponent of the + lowest positive power of ``10`` that multiplied by the literal produces an integer value. + ``mantissa`` is the result of that multiplication. + +``exponent`` is never negative and therefore must have an unsigned integer type. diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 60a11d6c3..96a937b11 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -8,6 +8,7 @@ The following are called value types because their variables will always be pass are used as function arguments or in assignments. .. index:: ! bool, ! true, ! false +.. _booleans: Booleans -------- @@ -523,6 +524,9 @@ regardless of the type of the right (exponent) operand. String Literals and Types ------------------------- +.. index:: ! literal;string concatenation +.. _string_literal_concatenation: + String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``), and they can also be split into multiple consecutive parts (``"foo" "bar"`` is equivalent to ``"foobar"``) which can be helpful when dealing with long strings. They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. For example, with ``bytes32 samevar = "stringliteral"`` the string literal is interpreted in its raw byte form when assigned to a ``bytes32`` type. @@ -565,6 +569,7 @@ Any Unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. .. index:: ! literal;unicode +.. _unicode_literals: Unicode Literals ---------------- @@ -576,7 +581,8 @@ They also support the very same escape sequences as regular string literals. string memory a = unicode"Hello 😃"; -.. index:: ! literal;hexadecimal, bytes +.. index:: ! literal;hexadecimal string, bytes +.. _hexadecimal_literals: Hexadecimal Literals -------------------- @@ -587,6 +593,8 @@ hexadecimal digits which can optionally use a single underscore as separator bet byte boundaries. The value of the literal will be the binary representation of the hexadecimal sequence. +.. index:: ! literal;hexadecimal string concatenation + Multiple hexadecimal literals separated by whitespace are concatenated into a single literal: ``hex"00112233" hex"44556677"`` is equivalent to ``hex"0011223344556677"`` diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 90decfad5..936fb1b3b 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -1,4 +1,5 @@ -.. index:: ! denomination +.. index:: ! denomination, literal;suffix +.. _denominations: ************************************** Units and Globally Available Variables