Closed lukewagner closed 4 years ago
Don't really have much to add here, sgtm. Agree that we should model things in one consistent fashion, so inline-blocks it is (if we ever want to change that we should do so globally).
and having the implicitly call to the lifted funcref execute as-if by
(call_indirect #coretypeidx)
I'd be curious to see the "implicit call to the lifted funcref" spelled out in more detail, I'm not sure where the call_indirect is specified. In particular I'm in favor of cutting out as many dependencies as possible, so I'd think we could start from there and expand into the typed refs in the future, rather than the other way round.
This is not consistent with how we have been writing adapters to date.
We do not separate 'before the call' from 'after the call'; primarily because this is not always that obvious.
You need to mirror what happens with export & import: there are two adapters for any given import/export pair. The same will be true for callback functions; because they are fundamentally symmetric with regular API calls.
You do still need the equivalent of func.bind. In part because the actual call to the callback will be at a later time and from 'deep in core wasm land'. In addition, the actual function that will be passed is inherently dynamic (in the sense that every call to an API that mentions a callback will potentially have a different callback function value).
Ah hah, I can see your point now! Yes, I suppose func.bind
is more general in that it allows you to capture an arbitrary set of state into the bound closure [edit: and also share values between the pre-call adapter instructions and post-call adapter instructions]. Can you give me a link to a PR or branch that contains a func.bind
example as you're imagining it; I can't find anything atm.
This has a use of func.bind https://github.com/WebAssembly/interface-types/blob/others/proposals/interface-types/working-notes/scenarios/fetch.md#export
I am working on another example, and it is a bit out of date, but should carry the idea.
Ok, thinking about it a bit more, yes, this makes sense. I'll close this issue then, and we can add closures in a separate PR/issue.
One bikeshedding thing: when we update the instruction, it'd be useful to rename it to match the lift
/lower
naming scheme, and avoid reusing func.bind
from function-references.
In the same way that array.lift and own use nested blocks instead of lambdas, I think we should frame the lifting/lowering of Interface Function values using nested expressions, for symmetry.
The lifting instruction would have the form:
and have type:
where
$F
is a type of the form(ref (func (param P*) (result R*)))
taken from#coretypeidx
$F'
is a type of the form(function (param P'*) (result R'*))
taken from#ittypeidx
lower-params
is valid with type[ P'* ] -> [ P* ]
lift-results
is valid with type[ R* ] -> [ R'* ]
Here the token
then
is a syntactic separator between thelower-params
andlift-results
child blocks, similar toelse
in core wasm. Intuitively speaking, the lifted core wasm function is call_ref'd at the point of thethen
(after parameters have been lowered and before results have been lifted).The lowering instruction would have the symmetric form:
and have type:
where
$F
is a type of the form(function (param P*) (result R*))
taken from#ittypeidx
$F'
is a type of the form(ref (func (param P'*) (result R'*)))
taken from#coretypeidx
lift-params
is valid with type[ P* ] -> [ P'* ]
lower-results
is valid with type[ R* ] -> [ R'* ]
When a
function.lift
andfunction.lower
are fused, their correspondinglift
/lower-params
andlift
/lower-results
blocks get fused, symmetric to what we've discussed with arrays and variants; the difference is that the resulting fused code goes into a new core function that gets logicallyfunc.bind
ed to each new function reference that flows through.Note, here I'm using typed function references, but we don't strictly block on that proposal: we could just as well depend only on reference-types changing the operand of
function.lift
and result offunction.lower
tofuncref
and having the implicitly call to the liftedfuncref
execute as-if by(call_indirect #coretypeidx)
. It'd be nicer to have the typed function references, though.