further steps to closure support
This commit is contained in:
parent
0d4c8ec70c
commit
632aece191
20 changed files with 379 additions and 138 deletions
|
|
@ -373,7 +373,7 @@ Design
|
|||
|
||||
A ``closure`` proc var can call ordinary procs of the default Nimrod calling
|
||||
convention. But not the other way round! A closure is implemented as a
|
||||
``tuple[prc, data]``. ``data`` can be nil implying a call without a closure.
|
||||
``tuple[prc, env]``. ``env`` can be nil implying a call without a closure.
|
||||
This means that a call through a closure generates an ``if`` but the
|
||||
interoperability is worth the cost of the ``if``. Thunk generation would be
|
||||
possible too, but it's slightly more effort to implement.
|
||||
|
|
@ -381,6 +381,18 @@ possible too, but it's slightly more effort to implement.
|
|||
Tests with GCC on Amd64 showed that it's really beneficical if the
|
||||
'environment' pointer is passed as the last argument, not as the first argument.
|
||||
|
||||
Proper thunk generation is harder because the proc that is to wrap
|
||||
could stem from a complex expression:
|
||||
|
||||
.. code-block:: nimrod
|
||||
receivesClosure(returnsDefaultCC[i])
|
||||
|
||||
A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require
|
||||
an *additional* closure generation... Ok, not really, but it requires to pass
|
||||
the function to call. So we'd end up with 2 indirect calls instead of one.
|
||||
Another much more severe problem which this solution is that it's not GC-safe
|
||||
to pass a proc pointer around via a generic ``ref`` type.
|
||||
|
||||
|
||||
Example code:
|
||||
|
||||
|
|
@ -492,3 +504,19 @@ Accumulator
|
|||
echo a() + b()
|
||||
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
Lambda lifting is implemented as part of the ``transf`` pass. The ``transf``
|
||||
pass generates code to setup the environment and to pass it around. However,
|
||||
this pass does not change the types! So we have some kind of mismatch here; on
|
||||
the one hand the proc expression becomes an explicit tuple, on the other hand
|
||||
the tyProc(ccClosure) type is not changed. For C code generation it's also
|
||||
important the hidden formal param is ``void*`` and not something more
|
||||
specialized. However the more specialized env type needs to passed to the
|
||||
backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain
|
||||
the formal hidden parameter, but not ``s.typ``!
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1037,7 +1037,7 @@ A `procedural type`:idx: is internally a pointer to a procedure. ``nil`` is
|
|||
an allowed value for variables of a procedural type. Nimrod uses procedural
|
||||
types to achieve `functional`:idx: programming techniques.
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
|
|
@ -1051,12 +1051,30 @@ Example:
|
|||
|
||||
forEach(printItem) # this will NOT work because calling conventions differ
|
||||
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
type
|
||||
TOnMouseMove = proc (x, y: int) {.closure.}
|
||||
|
||||
proc onMouseMove(mouseX, mouseY: int) =
|
||||
# has default calling convention
|
||||
echo "x: ", mouseX, " y: ", mouseY
|
||||
|
||||
proc setOnMouseMove(mouseMoveEvent: TOnMouseMove) = nil
|
||||
|
||||
# ok, 'onMouseMove' has the default calling convention, which is compatible
|
||||
# to 'closure':
|
||||
setOnMouseMove(onMouseMove)
|
||||
|
||||
|
||||
A subtle issue with procedural types is that the calling convention of the
|
||||
procedure influences the type compatibility: procedural types are only
|
||||
compatible if they have the same calling convention.
|
||||
compatible if they have the same calling convention. As a special extension,
|
||||
a procedure of the calling convention ``nimcall`` can be passed to a parameter
|
||||
that expects a proc of the calling convention ``closure``.
|
||||
|
||||
Nimrod supports these `calling conventions`:idx:, which are all incompatible to
|
||||
each other:
|
||||
Nimrod supports these `calling conventions`:idx:\:
|
||||
|
||||
`stdcall`:idx:
|
||||
This the stdcall convention as specified by Microsoft. The generated C
|
||||
|
|
@ -1089,8 +1107,10 @@ each other:
|
|||
same as ``fastcall``, but only for C compilers that support ``fastcall``.
|
||||
|
||||
`closure`:idx:
|
||||
indicates that the procedure expects a context, a closure that needs
|
||||
to be passed to the procedure.
|
||||
indicates that the procedure has a hidden implicit parameter
|
||||
(an *environment*). Proc vars that have the calling convention ``closure``
|
||||
take up two machine words: One for the proc pointer and another one for
|
||||
the pointer to implicitely passed environment.
|
||||
|
||||
`syscall`:idx:
|
||||
The syscall convention is the same as ``__syscall`` in C. It is used for
|
||||
|
|
@ -1114,6 +1134,11 @@ of the following conditions hold:
|
|||
The rules' purpose is to prevent the case that extending a non-``procvar``
|
||||
procedure with default parameters breaks client code.
|
||||
|
||||
The default calling convention is ``nimcall``, unless it is an inner proc (
|
||||
a proc inside of a proc). For an inner proc an analysis is performed wether it
|
||||
accesses its environment. If it does so, it has the calling convention
|
||||
``closure``, otherwise it has the calling convention ``nimcall``.
|
||||
|
||||
|
||||
Distinct type
|
||||
~~~~~~~~~~~~~
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue