Discussion: Permit static lambda assignment to function pointers #6746
Replies: 17 comments 31 replies
-
This requires a new feature which amends these parts of the 'static lambdas' speclet:
We will now need to guarantee that a 'static' lambda is emitted as 'static' if its address is taken.
Removing the 'static' modifier in a context where it is converted to function pointer will introduce a compile-time error. |
Beta Was this translation helpful? Give feedback.
-
Presuming, of course, that we would want to make those changes. We explicitly did not make any guarantees about how |
Beta Was this translation helpful? Give feedback.
-
To me, this feels like a feature gap in the language, something that you would expect to be possible but it doesn't work merely because it's not implemented. But I'd agree that it's not very common because function pointers hasn't been shipped yet ;) |
Beta Was this translation helpful? Give feedback.
-
That's not how I think about this at all. Function pointers aren't a common use case. They require unsafe, and getting a pointer requires taking the address of a static method. I wouldn't expect them to work with anything else. |
Beta Was this translation helpful? Give feedback.
-
Within that context, one could decide to use a function pointer instead of a delegate for performance reasons. Then they need to use a local function anywhere they previously used a (static) lambda. Now we're back where we felt the need for static lambdas in the first place (while static local functions were available). |
Beta Was this translation helpful? Give feedback.
-
I understand that technically a proper method is required to acquire an address, but this would be a convenient syntactic sugar much like what has been done for |
Beta Was this translation helpful? Give feedback.
-
Tbh why not just make the static lambda a private static member in the type then convert that to a function pointer and return it where you need to use it instead of using a |
Beta Was this translation helpful? Give feedback.
-
Wanted to address #6111 (comment) in the original issue (cc @HaloFour)
Func<int, int> del = static int (int x) => x;
delegate*<int, int> funcPtr = &static int (int x) => x; A lambda is only used once, so it seems reasonable that the way it is used could affect the way it is lowered. Why couldn't the compiler emit a non- |
Beta Was this translation helpful? Give feedback.
-
I'd like to see more of that in general, like if a lambda is only invoked from within the method in which it is declared the compiler could emit it as a local function instead, save an allocation or two. |
Beta Was this translation helpful? Give feedback.
-
I'm probably missing something but how it could possibly save any allocations? |
Beta Was this translation helpful? Give feedback.
-
I think @HaloFour's suggestion was to adjust lowering/emit in some cases so that no local variable is introduced and no delegate is actually allocated for the lambda. Func<int, int> multiply = x => x * 2;
Console.Write(multiply(2));
// equivalent to
int multiply(int x) => x * 2;
Console.Write(multiply(2)); |
Beta Was this translation helpful? Give feedback.
-
Got it. I thought it has something to do with |
Beta Was this translation helpful? Give feedback.
-
Considering this spec
I would find it useful to be able to directly assign a lambda to a function pointer without My use case is, I have a library that supports old versions of Unity (C# 4), and I conditionally compile with newer C# language features for better performance. A simplified example similar to what I'm doing: internal delegate void ConvertDelegate<T>(object asyncResult, ref T storage, int index);
internal Merger<T> : IAsyncResult<T>, IIndexedCallback
{
internal T _result;
private ConvertDelegate<T> _converter;
internal Merger(ConvertDelegate<T> converter)
{
_converter = converter;
}
void IIndexedCallback.Callback(object asyncResult, int index)
{
_result = _converter(asyncResult, ref _result, index);
}
T IAsyncResult<T>.Result { get { return _result; } }
}
public static AsyncResult<ValueTuple<T1, T2>> Merge<T1, T2>(AsyncResult<T1> result1, AsyncResult<T2> result2)
{
var merger = new Merger<ValueTuple<T1, T2>>((object asyncResult, ref ValueTuple<T1, T2> storage, int index) =>
{
if (index == 0)
{
storage.Item1 = ((IAsyncResult<T1>) asyncResult).Result;
}
else
{
storage.Item2 = ((IAsyncResult<T2>) asyncResult).Result;
}
});
result1.AddIndexedListener(merger, 0);
result2.AddIndexedListener(merger, 1);
return new AsyncResult<ValueTuple<T1, T2>>(merger);
}
...
public static AsyncResult<ValueTuple<T1, T2, T3, T4>> Merge<T1, T2, T3, T4>(AsyncResult<T1> result1, AsyncResult<T2> result2, AsyncResult<T3> result3, AsyncResult<T4> result4)
{
...
} And it would be very convenient for me if I could just change the #if CSHARP_12_OR_GREATER // Or whichever language version
private delegate*<object, ref T, int, void> _converter;
internal Merger(delegate*<object, ref T, int, void> converter)
{
_converter = converter;
}
#else
private ConvertDelegate<T> _converter;
internal Merger(ConvertDelegate<T> converter)
{
_converter = converter;
}
#endif I know my use case is super niche and this may be unlikely to happen, but I figured I should give my input here. |
Beta Was this translation helpful? Give feedback.
-
Being able to pass static lambdas to function pointers would be nice, but I must warn you: older Unity versions don't always handle |
Beta Was this translation helpful? Give feedback.
-
That's why I use conditional compilation for older Unity versions to just use the regular delegate. |
Beta Was this translation helpful? Give feedback.
-
delegate* unmanaged[Cdecl]<int, float> d = &static (i) => (float)i; This could also implicitly use UnmanagedCallersOnly when encountering an assignment to an unmanaged pointer, would be pretty useful with interop scenarios now that I think about it. |
Beta Was this translation helpful? Give feedback.
-
This will become significantly more useful for pointers to managed methods with me fixing dotnet/runtime#44610 (currently still working on it), since then function pointers become a mostly overhead-less way to pass things to call to other methods. |
Beta Was this translation helpful? Give feedback.
-
It should be safe to pass a static lambda to a function pointer.
Otherwise you must fallback to using local function and declare everything explicitly.
Beta Was this translation helpful? Give feedback.
All reactions