Skip to content

Commit bf6d708

Browse files
authored
Merge pull request #15721 from ethereum/document-internal-function-pointer-stability-guarantees
Document internal function pointer stability guarantees
2 parents d5d4558 + 4b1bf33 commit bf6d708

File tree

5 files changed

+93
-14
lines changed

5 files changed

+93
-14
lines changed

docs/control-structures.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ to limit the amount of gas.
239239
If the creation fails (due to out-of-stack, not enough balance or other problems),
240240
an exception is thrown.
241241

242+
.. _salted-contract-creations:
243+
242244
Salted contract creations / create2
243245
-----------------------------------
244246

docs/introduction-to-smart-contracts.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ operations, loops should be preferred over recursive calls. Furthermore,
537537
only 63/64th of the gas can be forwarded in a message call, which causes a
538538
depth limit of a little less than 1000 in practice.
539539

540+
.. _delegatecall:
540541
.. index:: delegatecall, library
541542

542543
Delegatecall and Libraries

docs/ir-breaking-changes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ hiding new and different behavior in existing code.
258258
Internals
259259
=========
260260

261+
.. _internal-function-pointers-in-ir:
262+
261263
Internal function pointers
262264
--------------------------
263265

@@ -276,6 +278,12 @@ The ID ``0`` is reserved for uninitialized function pointers which then cause a
276278
In the old code generator, internal function pointers are initialized with a special function that always causes a panic.
277279
This causes a storage write at construction time for internal function pointers in storage.
278280

281+
.. note::
282+
The compiler is free to omit internal functions that are never explicitly referenced by name.
283+
As a consequence, assigning to a function type variable in inline assembly does not guarantee
284+
that the assigned value will be included in the internal dispatch.
285+
The function must also be explicitly referenced elsewhere in the code.
286+
279287
Cleanup
280288
-------
281289

docs/security-considerations.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,16 @@ If your ``mapping`` information must be deleted, consider using a library simila
371371
`iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_,
372372
allowing you to traverse the keys and delete their values in the appropriate ``mapping``.
373373

374+
Internal Function Pointers in Upgradeable Contracts
375+
===================================================
376+
377+
Updating the code of your contract may :ref:`invalidate the values of variables of internal function
378+
types<function-type-value-stability-across-contract-updates>`.
379+
Consider such values ephemeral and avoid storing them in state variables.
380+
If you do, you must ensure that they never persist across code updates and are never used by
381+
other contracts having access to the same storage space as a result of a delegatecall or account
382+
abstraction.
383+
374384
Minor Details
375385
=============
376386

docs/types/value-types.rst

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ that has the same data representation as the input, whereas ``toUFixed256x18`` r
728728
Function Types
729729
--------------
730730

731-
Function types are the types of functions. Variables of function type
731+
Function types are the types of functions. Variables of a function type
732732
can be assigned from functions and function parameters of function type
733733
can be used to pass functions to and return functions from function calls.
734734
Function types come in two flavours - *internal* and *external* functions:
@@ -743,6 +743,20 @@ contract internally.
743743
External functions consist of an address and a function signature and they can
744744
be passed via and returned from external function calls.
745745

746+
Note that public functions of the current contract can be used both as an
747+
internal and as an external function. To use ``f`` as an internal function,
748+
just use ``f``, if you want to use its external form, use ``this.f``.
749+
750+
If a function type variable is not initialised, calling it results
751+
in a :ref:`Panic error<assert-and-require>`. The same happens if you call a function after using ``delete``
752+
on it.
753+
754+
.. note::
755+
Lambda or inline functions are planned but not yet supported.
756+
757+
Declaration syntax
758+
^^^^^^^^^^^^^^^^^^
759+
746760
Function types are notated as follows:
747761

748762
.. code-block:: solidity
@@ -759,7 +773,8 @@ omitted. Note that this only applies to function types. Visibility has
759773
to be specified explicitly for functions defined in contracts, they
760774
do not have a default.
761775

762-
Conversions:
776+
Conversions
777+
^^^^^^^^^^^
763778

764779
A function type ``A`` is implicitly convertible to a function type ``B`` if and only if
765780
their parameter types are identical, their return types are identical,
@@ -788,18 +803,10 @@ Which makes it possible to assign a ``payable`` function pointer to a ``non-paya
788803
function pointer ensuring both types behave the same way, i.e, both cannot be used
789804
to send ether.
790805

791-
If a function type variable is not initialised, calling it results
792-
in a :ref:`Panic error<assert-and-require>`. The same happens if you call a function after using ``delete``
793-
on it.
794-
795806
If external function types are used outside of the context of Solidity,
796807
they are treated as the ``function`` type, which encodes the address
797808
followed by the function identifier together in a single ``bytes24`` type.
798809

799-
Note that public functions of the current contract can be used both as an
800-
internal and as an external function. To use ``f`` as an internal function,
801-
just use ``f``, if you want to use its external form, use ``this.f``.
802-
803810
A function of an internal type can be assigned to a variable of an internal function type regardless
804811
of where it is defined.
805812
This includes private, internal and public functions of both contracts and libraries as well as free
@@ -828,7 +835,8 @@ Libraries are excluded because they require a ``delegatecall`` and use :ref:`a d
828835
convention for their selectors <library-selectors>`.
829836
Functions declared in interfaces do not have definitions so pointing at them does not make sense either.
830837

831-
Members:
838+
Members
839+
^^^^^^^
832840

833841
External (or public) functions have the following members:
834842

@@ -843,6 +851,59 @@ External (or public) functions have the following members:
843851
respectively. See :ref:`External Function Calls <external-function-calls>` for
844852
more information.
845853

854+
.. _function-type-value-stability-across-contract-updates:
855+
856+
Value stability across contract updates
857+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
858+
859+
An important aspect to consider when using values of function types is whether the value will
860+
remain valid if the underlying code changes.
861+
862+
The state of the blockchain is not completely immutable and there are multiple ways to place
863+
different code under the same address:
864+
865+
- Directly deploying different code using :ref:`salted contract creation<salted-contract-creations>`.
866+
- Delegating to a different contract via :ref:`DELEGATECALL<delegatecall>`
867+
(upgradeable code behind a proxy contract is a common example of this).
868+
- Account abstraction as defined by `EIP-7702 <https://eips.ethereum.org/EIPS/eip-7702>`_.
869+
870+
External function types can be considered as stable as contract's ABI, which makes them very portable.
871+
Their ABI representation always consists of a contract address and a function selector and it is
872+
perfectly safe to store them long-term or pass them between contracts.
873+
While it is possible for the referenced function to change or disappear, a direct external call
874+
would be affected the same way, so there is no additional risk in such use.
875+
876+
In case of internal functions, however, the value is an identifier that is strongly tied to
877+
contract's bytecode.
878+
The actual representation of the identifier is an implementation detail and may change between
879+
compiler versions or even :ref:`between different backends<internal-function-pointers-in-ir>`.
880+
Values assigned under a given representation are deterministic (i.e. guaranteed to remain the same
881+
as long as the source code is the same) but are easily affected by changes such as adding, removing
882+
or reordering of functions.
883+
The compiler is also free to remove internal functions that are never used, which may affect other identifiers.
884+
Some representations, e.g. one where identifiers are simply jump targets, may be affected by
885+
virtually any change, even one completely unrelated to internal functions.
886+
887+
To counter this, the language limits the use of internal function types outside of the context in
888+
which they are valid.
889+
This is why internal function types cannot be used as parameters of external functions (or in any
890+
other way that is exposed in contract's ABI).
891+
However, there are still situations where it is up to the user to decide whether their use is safe or not.
892+
For example long-term storage of such values in state variables is discouraged, but may be safe if
893+
the contract code is never going to be updated.
894+
It is also always possible to side-step any safeguards by using inline assembly.
895+
Such use always needs careful consideration.
896+
897+
.. note::
898+
The removal of unused internal functions only takes into account explicit references to
899+
such functions by name.
900+
Implicit references, such as assigning a new value to a function type variable in inline assembly
901+
may still lead to the removal of the function if it is not also referenced explicitly elsewhere
902+
in the source.
903+
904+
Examples
905+
^^^^^^^^
906+
846907
Example that shows how to use the members:
847908

848909
.. code-block:: solidity
@@ -966,6 +1027,3 @@ Another example that uses external function types:
9661027
exchangeRate = response;
9671028
}
9681029
}
969-
970-
.. note::
971-
Lambda or inline functions are planned but not yet supported.

0 commit comments

Comments
 (0)