mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
docs: Literal suffixes
This commit is contained in:
parent
2bf76afec1
commit
edcc3af9c8
@ -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<literal_suffixes>` definition.
|
||||
|
@ -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<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
|
||||
|
@ -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<literal_suffixes>` 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<fractional_decomposition>` is performed
|
||||
first in order to obtain the two input values.
|
||||
|
||||
See the section on :ref:`calling suffix functions<calling_suffix_functions>` for more information
|
||||
on the limitations of this call syntax.
|
||||
|
||||
|
||||
.. index:: ! new, contracts;creating
|
||||
|
||||
|
@ -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
|
||||
|
269
docs/types/literal-suffixes.rst
Normal file
269
docs/types/literal-suffixes.rst
Normal file
@ -0,0 +1,269 @@
|
||||
.. index:: ! literal suffix
|
||||
.. _literal_suffixes:
|
||||
|
||||
Literal Suffixes
|
||||
================
|
||||
|
||||
While Solidity :ref:`provides implicit conversions<types-conversion-literals>` 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<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<denominations>`:
|
||||
|
||||
.. 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<suffix_function_parameters>` are also invoked with
|
||||
one literal - the :ref:`decomposition<fractional_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<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<suffix_function_parameters>` 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<overload-function>`.
|
||||
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<functions>`.
|
||||
|
||||
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<rational_literals>` can optionally accept two arguments,
|
||||
produced by the :ref:`fractional decomposition<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<booleans>` literals |
|
||||
+-------------------------------------------------------------+----------------------------------------------------------------+
|
||||
| ``uint8``, ..., ``uint256``, ``int8``, ..., ``int256`` | - :ref:`Rational literals<rational_literals>` (including zero) |
|
||||
| | - Hexadecimal number literals |
|
||||
+-------------------------------------------------------------+----------------------------------------------------------------+
|
||||
| ``address`` | - :ref:`Address literals<address_literals>` |
|
||||
+-------------------------------------------------------------+----------------------------------------------------------------+
|
||||
| ``bytes1``, ..., ``bytes32`` | - Hexadecimal number literals (not for ``bytes20``) |
|
||||
| | - :ref:`Hexadecimal string literals<hexadecimal_literals>` |
|
||||
| | - :ref:`String literals<string_literals>` |
|
||||
| | - :ref:`Unicode literals<unicode_literals>` |
|
||||
| | - :ref:`Zero<rational_literals>` |
|
||||
+-------------------------------------------------------------+----------------------------------------------------------------+
|
||||
| ``bytes`` | - :ref:`Hexadecimal string literals<hexadecimal_literals>` |
|
||||
| | - :ref:`String literals<string_literals>` |
|
||||
| | - :ref:`Unicode literals<unicode_literals>` |
|
||||
+-------------------------------------------------------------+----------------------------------------------------------------+
|
||||
| ``string`` | - :ref:`String literals<string_literals>` |
|
||||
| | - :ref:`Unicode literals<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<calling_suffix_functions_with_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.
|
@ -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"``
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
.. index:: ! denomination
|
||||
.. index:: ! denomination, literal;suffix
|
||||
.. _denominations:
|
||||
|
||||
**************************************
|
||||
Units and Globally Available Variables
|
||||
|
Loading…
Reference in New Issue
Block a user