What is a `thunk`?

I've been following along with various proposals in evolution, the most recent being the distributed actor runtime pitch, and seen the term thunk used. I've also seen it when there's been discussion about synthesized code from the compiler - and while I think I understand it, I wanted to ask to verify: What is a thunk?

I've inferred that it's a segment of code - likely generated - and possibly an atomic unit of code that might be composed, but I'm otherwise unfamiliar.

Apologies if this is off-topic - this seemed to be the best place to ask the question of the categories in the forums.

8 Likes

A thunk is a piece of code that deals with an impedance mismatch between caller and callee.

Perhaps an example from the Windows world might be easier to understand:

As Windows evolved from 16 bit -> 32 bit -> 64 bit, it was necessary for new code to call older code, and in some circumstances, the reverse.

Take an app written for Windows 3.1. It would run unmodified on Windows 95, even though some of the system APIs were written as 32-bit code. How does the 16-bit code, which is passing 16-bit wide words, call the new 32-bit code? The answer is that MS introduced thunks for the 16-bit entry points which widened the parameters before calling the new 32-bit implementation and then narrowed the return type to 16-bits, if necessary.

14 Likes

Another concrete example is the thunks generated for Objective C interoperability.

Swift has several different method dispatch mechanisms:

  1. Swift methods can be fully inlined so there's actually no method call at all (IDK if this counts as a dispatch mechanism, but worth mentioning)
  2. Swift methods can be dynamically dispatched using a v-table (e.g. when using classes or protocol existentials)
  3. Swift methods can be dynamically dispatched via message passing (i.e. dynamically looked up by a selector)

Message passing is the only mechanism supported by Objective C (well, until recently). Swift supports it, but it would be a drag if a class' members always had to be dynamically dispatched, just because the author also wanted that class to be accessible from Objective C.

So instead, the compiler generates thunks for @objc methods. These thunks are what get invoked when you call the methods from Objective C (well, from Swift as well, if you use ObjC runtime APIs like performSelector or objc_msgSend). These thunks just do a static dispatch to the "real" Swift implementation of the method. They can also account for any discrepancies in the calling conventions between Swift and ObjC, such as their different referencing counting conventions.

All the while, when you call the Swift code from Swift, you can do regular static/v-table dispatch, skipping all the message passing and thunks.

Here's some more info from @beccadax:

9 Likes

The answers above are great but in the simplest and most general terms a thunk is a closure that wraps a value or other computations to hide complexity and provide lazy evaluation. @autoclosure is a great example of a thunk: it wraps a function argument in a closure which can be invoked if needed. The work to compute the argument is both hidden and delayed until the argument value is needed, if at all.

12 Likes

BTW, if one stumbles upon a similarly unclear term, there's a Lexicon file in the compiler docs. "Thunk" is already defined there.

16 Likes

Thanks for this @Max_Desiatov :slight_smile: I keep forgetting there's a lot of great info buried in the docs.

2 Likes