What I meant was that a function or procedure that takes an anonymous function as an argument, and specifically does not capture state but simply makes use of the return value, is negatively impacted performance-wise if the anonymous function still requires heap allocation no matter what.
But the code that gets passed such an anonymous function can not differentiate whether the function got state or not. In fact in one case it can be called with a function reference that has state and in another with one that doesn't. So there is only one way that the code calling the anonymous function can call said function: by treating the function reference as an interface and calling
Invoke.
ONLY if the compiler could proof that
ONLY anonymous functions without state are passed to that other code could it change the function reference to a mere function pointer.
This restricts the scope of such an optimization quite a bit:
- the code would all need to reside in the
implementation section of a unit (or completely inside the main program file)
- or all units are compiled as part of a WPO pass cause as soon as one participating function is part of the
interface section the compiler
must not do such optimizations as the unit might be used as part of a program where a function with state is passed in
It is simply not true that there is any legitimate technical reason that function pointers cannot be inlined, though. I can't really think of a compiler for any "native" language other than FPC (and Delphi) that doesn't do so. (I've actually looked into what it would take to implement it in FPC myself, and the only thing that seems to make it difficult at all is the fact that "procvardefs" have an overly-limited amount of information associated with them versus "procdefs".)
For example, here's a small C++ proram showing that both C++11 lambdas and also "classic" function pointers are indeed fully inlineable:
https://godbolt.org/z/BPdOI6
Yes, there is a technical reason: namely if the compiler can not proof that it can simplify it. Your example is an explicit situation where it would also be possible in FPC (if the compiler would support it). Even your example would however fail if you'd split the functions across compilation units (with Link Time Code Generation or whatever the term is for LLVM disabled).
These examples however are highly restricted (see my points mentioned above). And no, only because C++ compilers are able to do this does not mean that FPC can do this as well, because both languages involve different compilation concepts (we have precompiled units with a defined interface while in C++ you only have header files that are essentially "external" declarations). The unit concept simply prohibits some optimizations, because units can be shared between programs (Visual Studio for example complains if you share the immediate build directory (where the .obj files are put in) is shared between projects, because one project might overwrite the other project's files).
The same program in Swift: https://godbolt.org/z/72uzPw
And in Rust: https://godbolt.org/z/7h0ssB
Also in C (with no anonymous function obviously, just the function pointer): https://godbolt.org/z/hX76pQ
And lastly in D: https://godbolt.org/z/N5o46p
In all cases, no heap allocation is required for the non-capturing anonymous function, and the use of both it as well as the "normal" function pointer is inlined completely.
Again same problem as in C++. As soon as you get to more real world examples and also apply the conceptual restrictions of Object Pascal that all breaks apart.