further steps to closure support

This commit is contained in:
Araq 2012-02-06 00:19:56 +01:00
commit 632aece191
20 changed files with 379 additions and 138 deletions

View file

@ -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``!

View file

@ -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
~~~~~~~~~~~~~