Update function pointer proposal with LDM changes (#2923)
* Update function pointer proposal with LDM changes * Remove unnecessary section about calling convention
This commit is contained in:
parent
e14bb331fd
commit
08e94b3029
|
@ -1,11 +1,13 @@
|
||||||
# Function Pointers
|
# Function Pointers
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
This proposal provides language constructs that expose IL opcodes that cannot currently be accessed efficiently,
|
This proposal provides language constructs that expose IL opcodes that cannot currently be accessed efficiently,
|
||||||
or at all, in C# today: `ldftn` and `calli`. These IL opcodes can be important in high performance code and developers
|
or at all, in C# today: `ldftn` and `calli`. These IL opcodes can be important in high performance code and developers
|
||||||
need an efficient way to access them.
|
need an efficient way to access them.
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
The motivations and background for this feature are described in the following issue (as is a
|
The motivations and background for this feature are described in the following issue (as is a
|
||||||
potential implementation of the feature):
|
potential implementation of the feature):
|
||||||
|
|
||||||
|
@ -17,44 +19,43 @@ This is an alternate design proposal to [compiler intrinsics]
|
||||||
## Detailed Design
|
## Detailed Design
|
||||||
|
|
||||||
### Function pointers
|
### Function pointers
|
||||||
The language will allow for the declaration of function pointers using the `func*` syntax. The full syntax is described
|
|
||||||
in detail in the next section but it is meant to resemble the syntax used by `delegate` declarations.
|
The language will allow for the declaration of function pointers using the `delegate*` syntax. The full syntax is described
|
||||||
|
in detail in the next section but it is meant to resemble the syntax used by `Func` and `Action` type declarations.
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
unsafe class Example {
|
unsafe class Example {
|
||||||
delegate void DAction(int a);
|
void Example(Action<int> a, delegate*<int, void> f) {
|
||||||
|
a(42);
|
||||||
void Example(DAction d, func* void(int) f) {
|
|
||||||
d(42);
|
|
||||||
f(42);
|
f(42);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
These types are represented using the function pointer type as outlined in ECMA-335. This means invocation
|
These types are represented using the function pointer type as outlined in ECMA-335. This means invocation
|
||||||
of a `func*` will use `calli` where invocation of a `delegate` will use `callvirt` on the `Invoke` method.
|
of a `delegate*` will use `calli` where invocation of a `delegate` will use `callvirt` on the `Invoke` method.
|
||||||
Syntactically though invocation is identical for both constructs.
|
Syntactically though invocation is identical for both constructs.
|
||||||
|
|
||||||
The ECMA-335 definition of method pointers includes the calling convention as part of the type signature (section 7.1).
|
The ECMA-335 definition of method pointers includes the calling convention as part of the type signature (section 7.1).
|
||||||
The default calling convention will be `managed`. Alternate forms can be specified by adding the appropriate modifier
|
The default calling convention will be `managed`. Alternate forms can be specified by adding the appropriate modifier
|
||||||
after the `func*` syntax: `cdecl`, `fastcall`, `stdcall`, `thiscall` or `winapi`. Example:
|
after the `delegate*` syntax: `managed`, `cdecl`, `stdcall`, `thiscall`, or `unmanaged`. Example:
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
// This method will be invoked using the cdecl calling convention
|
// This method will be invoked using the cdecl calling convention
|
||||||
func* cdecl int(int value);
|
delegate* cdecl<int, int>;
|
||||||
|
|
||||||
// This method will be invoked using the stdcall calling convention
|
// This method will be invoked using the stdcall calling convention
|
||||||
func* stdcall int(int value);
|
delegate* stdcall<int, int>;
|
||||||
```
|
```
|
||||||
|
|
||||||
Conversions between `func*` types is done based on their signature including the calling convention.
|
Conversions between `delegate*` types is done based on their signature including the calling convention.
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
unsafe class Example {
|
unsafe class Example {
|
||||||
void Conversions() {
|
void Conversions() {
|
||||||
func* int(int, int) p1 = ...;
|
delegate*<int, int, int> p1 = ...;
|
||||||
func* managed int(int, int) p2 = ...;
|
delegate* managed<int, int, int> p2 = ...;
|
||||||
func* cdecl int(int, int) p3 = ...;
|
delegate* cdecl<int, int, int> p3 = ...;
|
||||||
|
|
||||||
p1 = p2; // okay p1 and p2 have compatible signatures
|
p1 = p2; // okay p1 and p2 have compatible signatures
|
||||||
Console.WriteLine(p2 == p1); // True
|
Console.WriteLine(p2 == p1); // True
|
||||||
|
@ -63,81 +64,76 @@ unsafe class Example {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A `func*` type is a pointer type which means it has all of the capabilities and restrictions of a standard pointer
|
A `delegate*` type is a pointer type which means it has all of the capabilities and restrictions of a standard pointer
|
||||||
type:
|
type:
|
||||||
|
|
||||||
- Only valid in an `unsafe` context.
|
- Only valid in an `unsafe` context.
|
||||||
- Methods which contain a `func*` parameter or return type can only be called from an `unsafe` context.
|
- Methods which contain a `delegate*` parameter or return type can only be called from an `unsafe` context.
|
||||||
- Cannot be converted to `object`.
|
- Cannot be converted to `object`.
|
||||||
- Cannot be used as a generic argument.
|
- Cannot be used as a generic argument.
|
||||||
- Can implicitly convert `func*` to `void*`.
|
- Can implicitly convert `delegate*` to `void*`.
|
||||||
- Can explicitly convert from `void*` to `func*`.
|
- Can explicitly convert from `void*` to `delegate*`.
|
||||||
|
|
||||||
Restrictions:
|
Restrictions:
|
||||||
- Custom attributes cannot be applied to a `func*` or any of its elements.
|
|
||||||
- A `func*` parameter cannot be marked as `params`
|
- Custom attributes cannot be applied to a `delegate*` or any of its elements.
|
||||||
- A `func*` type has all of the restrictions of a normal pointer type.
|
- A `delegate*` parameter cannot be marked as `params`
|
||||||
|
- A `delegate*` type has all of the restrictions of a normal pointer type.
|
||||||
|
|
||||||
### Function pointer syntax
|
### Function pointer syntax
|
||||||
|
|
||||||
The full function pointer syntax is represented by the following grammar:
|
The full function pointer syntax is represented by the following grammar:
|
||||||
|
|
||||||
```
|
```antlr
|
||||||
funcptr_type =
|
pointer_type
|
||||||
'func' '*' [calling_convention] type method_arglist |
|
: ...
|
||||||
'(' funcptr_type ')' ;
|
| funcptr_type
|
||||||
|
;
|
||||||
|
|
||||||
calling_convention =
|
funcptr_type
|
||||||
'cdecl' |
|
: 'delegate' '*' calling_convention? '<' (funcptr_parameter_modifier? type ',')* funcptr_return_modifier? return_type '>'
|
||||||
'managed' |
|
;
|
||||||
'stdcall' |
|
|
||||||
'thiscall' |
|
calling_convention
|
||||||
'unmanaged' ;
|
: 'cdecl'
|
||||||
|
| 'managed'
|
||||||
|
| 'stdcall'
|
||||||
|
| 'thiscall'
|
||||||
|
| 'unmanaged'
|
||||||
|
;
|
||||||
|
|
||||||
|
funcptr_parameter_modifier
|
||||||
|
: 'ref'
|
||||||
|
| 'out'
|
||||||
|
| 'in'
|
||||||
|
;
|
||||||
|
|
||||||
|
funcptr_return_modifier
|
||||||
|
: 'ref'
|
||||||
|
| 'ref readonly'
|
||||||
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
The `unmanaged` calling convention represents the default calling convention for native code on the current platform, and is encoded as winapi.
|
The `unmanaged` calling convention represents the default calling convention for native code on the current platform, and is encoded as winapi.
|
||||||
|
All `calling_convention`s are contextual keywords when preceded by a `delegate*`.
|
||||||
When there is a nested function pointer, a function pointer which has or returns a function pointer, parens can be
|
|
||||||
optionally used to disambiguate the signature. Though they are not required and the resulting types are equivalent.
|
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
delegate int Func1(string s);
|
delegate int Func1(string s);
|
||||||
delegate Func1 Func2(Func1 f);
|
delegate Func1 Func2(Func1 f);
|
||||||
|
|
||||||
// Function pointer equivalent without parens or calling convention
|
// Function pointer equivalent without calling convention
|
||||||
func* int(string);
|
delegate*<string, int>;
|
||||||
func* func* int(string) int(func* int(string));
|
delegate*<delegate*<string, int>, delegate*<string, int>>;
|
||||||
|
|
||||||
// Function pointer equivalent without parens and with calling convention
|
// Function pointer equivalent with calling convention
|
||||||
func* managed int(string);
|
delegate* managed<string, int>;
|
||||||
func* managed func* managed int(string) int(func* managed int(string));
|
delegate*<delegate* managed<string, int>, delegate*<string, int>>;
|
||||||
|
|
||||||
// Function pointer equivalent with parens and without calling convention
|
|
||||||
func* int(string);
|
|
||||||
func* (func* int(string)) int((func* int(string));
|
|
||||||
|
|
||||||
// Function pointer equivalent of with parens and calling convention
|
|
||||||
func* int(string)
|
|
||||||
func* managed (func* managed int(string)) int((func* managed int(string));
|
|
||||||
```
|
|
||||||
|
|
||||||
When the calling convention is omitted from the syntax then `managed` will be used as the calling convention. That means
|
|
||||||
all of the forms of `Func1` and `Func2` defined above are equivalent signatures.
|
|
||||||
|
|
||||||
The calling convention cannot be omitted when the return type of the function pointer has the same name as a calling
|
|
||||||
convention. In that case, the parser would process the return type as a calling convention instead of a type. To resolve
|
|
||||||
this the developer must specify both the calling convention and the return type.
|
|
||||||
|
|
||||||
``` csharp
|
|
||||||
class cdecl { }
|
|
||||||
|
|
||||||
// Function pointer which has a cdecl calling convention, a cdecl return type and takes a single
|
|
||||||
// parameter of type cdecl;
|
|
||||||
func* cdecl cdecl(cdecl);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Allow address-of to target methods
|
### Allow address-of to target methods
|
||||||
|
|
||||||
Method groups will now be allowed as arguments to an address-of expression. The type of such an
|
Method groups will now be allowed as arguments to an address-of expression. The type of such an
|
||||||
expression will be a `func*` which has the equivalent signature of the target method and a managed
|
expression will be a `delegate*` which has the equivalent signature of the target method and a managed
|
||||||
calling convention:
|
calling convention:
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
|
@ -145,10 +141,10 @@ unsafe class Util {
|
||||||
public static void Log() { }
|
public static void Log() { }
|
||||||
|
|
||||||
void Use() {
|
void Use() {
|
||||||
func* void() ptr1 = &Util.Log;
|
delegate*<void> ptr1 = &Util.Log;
|
||||||
|
|
||||||
// Error: type "func* void()" not compatible with "func int()";
|
// Error: type "delegate*<void>" not compatible with "delegate*<int>";
|
||||||
func* int() ptr2 = &Util.Log;
|
delegate*<int> ptr2 = &Util.Log;
|
||||||
|
|
||||||
// Okay. Conversion to void* is always allowed.
|
// Okay. Conversion to void* is always allowed.
|
||||||
void* v = &Util.Log;
|
void* v = &Util.Log;
|
||||||
|
@ -156,10 +152,11 @@ unsafe class Util {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The conversion of an address-of method group to `func*` has roughly the same process as method group to `delegate`
|
The conversion of an address-of method group to `delegate*` has roughly the same process as method group to `delegate`
|
||||||
conversion. There are two additional restrictions to the existing process:
|
conversion. There are two additional restrictions to the existing process:
|
||||||
|
|
||||||
- Only members of the method group that are marked as `static` will be considered.
|
- Only members of the method group that are marked as `static` will be considered.
|
||||||
- Only a `func*` with a managed calling convention can be the target of such a conversion.
|
- Only a `delegate*` with a managed calling convention can be the target of such a conversion.
|
||||||
|
|
||||||
This means developers can depend on overload resolution rules to work in conjunction with the
|
This means developers can depend on overload resolution rules to work in conjunction with the
|
||||||
address-of operator:
|
address-of operator:
|
||||||
|
@ -171,8 +168,8 @@ unsafe class Util {
|
||||||
public static void Log(int i) { };
|
public static void Log(int i) { };
|
||||||
|
|
||||||
void Use() {
|
void Use() {
|
||||||
func* void() a1 = &Log; // Log()
|
delegate*<void> a1 = &Log; // Log()
|
||||||
func* void(int) a2 = &Log; // Log(int i)
|
delegate*<int, void> a2 = &Log; // Log(int i)
|
||||||
|
|
||||||
// Error: ambiguous conversion from method group Log to "void*"
|
// Error: ambiguous conversion from method group Log to "void*"
|
||||||
void* v = &Log;
|
void* v = &Log;
|
||||||
|
@ -182,27 +179,30 @@ unsafe class Util {
|
||||||
The address-of operator will be implemented using the `ldftn` instruction.
|
The address-of operator will be implemented using the `ldftn` instruction.
|
||||||
|
|
||||||
Restrictions of this feature:
|
Restrictions of this feature:
|
||||||
|
|
||||||
- Only applies to methods marked as `static`.
|
- Only applies to methods marked as `static`.
|
||||||
- Non-`static` local functions cannot be used in `&`. The implementation details of these methods are
|
- Non-`static` local functions cannot be used in `&`. The implementation details of these methods are
|
||||||
deliberately not specified by the language. This includes whether they are static vs. instance or
|
deliberately not specified by the language. This includes whether they are static vs. instance or
|
||||||
exactly what signature they are emitted with.
|
exactly what signature they are emitted with.
|
||||||
|
|
||||||
### Better function member
|
### Better function member
|
||||||
|
|
||||||
The better function member specification will be changed to include the following line:
|
The better function member specification will be changed to include the following line:
|
||||||
|
|
||||||
> A `func*` is more specific than `void*`
|
> A `delegate*` is more specific than `void*`
|
||||||
|
|
||||||
This means that it is possible to overload on `void*` and a `func*` and still sensibly use the address-of operator.
|
This means that it is possible to overload on `void*` and a `delegate*` and still sensibly use the address-of operator.
|
||||||
|
|
||||||
## Open Issues
|
## Open Issues
|
||||||
|
|
||||||
### NativeCallableAttribute
|
### NativeCallableAttribute
|
||||||
|
|
||||||
This is an attribute used by the CLR to avoid the managed to native prologue when invoking. Methods marked by this
|
This is an attribute used by the CLR to avoid the managed to native prologue when invoking. Methods marked by this
|
||||||
attribute are only callable from native code, not managed (can’t call methods, create a delegate, etc …). The attribute
|
attribute are only callable from native code, not managed (can’t call methods, create a delegate, etc …). The attribute
|
||||||
is not special to mscorlib; the runtime will treat any attribute with this name with the same semantics.
|
is not special to mscorlib; the runtime will treat any attribute with this name with the same semantics.
|
||||||
|
|
||||||
It's possible for the runtime and language to work together to fully support this. The language could choose to treat
|
It's possible for the runtime and language to work together to fully support this. The language could choose to treat
|
||||||
address-of `static` members with a `NativeCallable` attribute as a `func*` with the specified calling convention.
|
address-of `static` members with a `NativeCallable` attribute as a `delegate*` with the specified calling convention.
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
unsafe class NativeCallableExample {
|
unsafe class NativeCallableExample {
|
||||||
|
@ -210,9 +210,9 @@ unsafe class NativeCallableExample {
|
||||||
static void CloseHandle(IntPtr p) => Marshal.FreeHGlobal(p);
|
static void CloseHandle(IntPtr p) => Marshal.FreeHGlobal(p);
|
||||||
|
|
||||||
void Use() {
|
void Use() {
|
||||||
func* void(IntPtr) p1 = &CloseHandle; // Error: Invalid calling convention
|
delegate*<IntPtr, void> p1 = &CloseHandle; // Error: Invalid calling convention
|
||||||
|
|
||||||
func* cdecl void(IntPtr) p2 = &CloseHandle; // Okay
|
delegate* cdecl<IntPtr, void> p2 = &CloseHandle; // Okay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,12 +225,12 @@ managed code the compiler should prevent developers from attempting such an invo
|
||||||
- Prevent method group conversions to `delegate` when the method is tagged with `NativeCallable`.
|
- Prevent method group conversions to `delegate` when the method is tagged with `NativeCallable`.
|
||||||
|
|
||||||
This is not necessary to support `NativeCallable` though. The compiler can support the `NativeCallable` attribute as is
|
This is not necessary to support `NativeCallable` though. The compiler can support the `NativeCallable` attribute as is
|
||||||
using the existing syntax. The program would simply need to cast to `void*` before casting to the correct `func*`
|
using the existing syntax. The program would simply need to cast to `void*` before casting to the correct `delegate*`
|
||||||
signature. That would be no worse than the support today.
|
signature. That would be no worse than the support today.
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
void* v = &CloseHandle;
|
void* v = &CloseHandle;
|
||||||
func* cdecl bool(IntPtr) f1 = (func* cdecl bool(IntPtr))v;
|
delegate* cdecl<IntPtr, bool> f1 = (delegate* cdecl<IntPtr, bool>)v;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Extensible set of unmanaged calling conventions
|
### Extensible set of unmanaged calling conventions
|
||||||
|
@ -253,6 +253,7 @@ conventions is very rich.
|
||||||
## Considerations
|
## Considerations
|
||||||
|
|
||||||
### Allow instance methods
|
### Allow instance methods
|
||||||
|
|
||||||
The proposal could be extended to support instance methods by taking advantage of the `EXPLICITTHIS` CLI calling
|
The proposal could be extended to support instance methods by taking advantage of the `EXPLICITTHIS` CLI calling
|
||||||
convention (named `instance` in C# code). This form of CLI function pointers puts the `this` parameter as an explicit
|
convention (named `instance` in C# code). This form of CLI function pointers puts the `this` parameter as an explicit
|
||||||
first parameter of the function pointer syntax.
|
first parameter of the function pointer syntax.
|
||||||
|
@ -260,7 +261,7 @@ first parameter of the function pointer syntax.
|
||||||
``` csharp
|
``` csharp
|
||||||
unsafe class Instance {
|
unsafe class Instance {
|
||||||
void Use() {
|
void Use() {
|
||||||
func* instance string(Instance) f = &ToString;
|
delegate* instance<Instance, string> f = &ToString;
|
||||||
f(this);
|
f(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,15 +276,16 @@ simple work around: use a `static` local function.
|
||||||
unsafe class Instance {
|
unsafe class Instance {
|
||||||
void Use() {
|
void Use() {
|
||||||
static string toString(Instance i) = i.ToString();
|
static string toString(Instance i) = i.ToString();
|
||||||
func* string(Instance) f = &toString;
|
delgate*<Instance, string> f = &toString;
|
||||||
f(this);
|
f(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Don't require unsafe at declaration
|
### Don't require unsafe at declaration
|
||||||
Instead of requiring `unsafe` at every use of a `func*`, only require it at the point where a method group is
|
|
||||||
converted to a `func*`. This is where the core safety issues come into play (knowing that the containing assembly
|
Instead of requiring `unsafe` at every use of a `delegate*`, only require it at the point where a method group is
|
||||||
|
converted to a `delegate*`. This is where the core safety issues come into play (knowing that the containing assembly
|
||||||
cannot be unloaded while the value is alive). Requiring `unsafe` on the other locations can be seen as excessive.
|
cannot be unloaded while the value is alive). Requiring `unsafe` on the other locations can be seen as excessive.
|
||||||
|
|
||||||
This is how the design was originally intended. But the resulting language rules felt very awkward. It's impossible to
|
This is how the design was originally intended. But the resulting language rules felt very awkward. It's impossible to
|
||||||
|
@ -291,20 +293,21 @@ hide the fact that this is a pointer value and it kept peeking through even with
|
||||||
the conversion to `object` can't be allowed, it can't be a member of a `class`, etc ... The C# design is to require
|
the conversion to `object` can't be allowed, it can't be a member of a `class`, etc ... The C# design is to require
|
||||||
`unsafe` for all pointer uses and hence this design follows that.
|
`unsafe` for all pointer uses and hence this design follows that.
|
||||||
|
|
||||||
Developers will still be capable of preventing a _safe_ wrapper on top of `func*` values the same way that they do
|
Developers will still be capable of preventing a _safe_ wrapper on top of `delegate*` values the same way that they do
|
||||||
for normal pointer types today. Consider:
|
for normal pointer types today. Consider:
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
unsafe struct Action {
|
unsafe struct Action {
|
||||||
func* void() _ptr;
|
delegate*<void> _ptr;
|
||||||
|
|
||||||
Action(func* void() ptr) => _ptr = ptr;
|
Action(delegate*<void> ptr) => _ptr = ptr;
|
||||||
public void Invoke() => _ptr();
|
public void Invoke() => _ptr();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using delegates
|
### Using delegates
|
||||||
Instead of using a new syntax element, `func*`, simply use existing `delegate` types with a `*` following the type:
|
|
||||||
|
Instead of using a new syntax element, `delegate*`, simply use existing `delegate` types with a `*` following the type:
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
Func<object, object, bool>* ptr = &object.ReferenceEquals;
|
Func<object, object, bool>* ptr = &object.ReferenceEquals;
|
||||||
|
@ -327,6 +330,7 @@ One option that was explored was emitting such a pointer as `mod_req(Func<int>)
|
||||||
work though as a `mod_req` cannot bind to a `TypeSpec` and hence cannot target generic instantiations.
|
work though as a `mod_req` cannot bind to a `TypeSpec` and hence cannot target generic instantiations.
|
||||||
|
|
||||||
### Named function pointers
|
### Named function pointers
|
||||||
|
|
||||||
The function pointer syntax can be cumbersome, particularly in complex cases like nested function pointers. Rather than
|
The function pointer syntax can be cumbersome, particularly in complex cases like nested function pointers. Rather than
|
||||||
have developers type out the signature every time the language could allow for named declarations of function pointers
|
have developers type out the signature every time the language could allow for named declarations of function pointers
|
||||||
as is done with `delegate`.
|
as is done with `delegate`.
|
||||||
|
@ -359,7 +363,7 @@ class NamedTupleExample {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
After discussion we decided to not allow named declaration of `func*` types. If we find there is significant need for
|
After discussion we decided to not allow named declaration of `delegate*` types. If we find there is significant need for
|
||||||
this based on customer usage feedback then we will investigate a naming solution that works for function pointers,
|
this based on customer usage feedback then we will investigate a naming solution that works for function pointers,
|
||||||
tuples, generics, etc ... This is likely to be similar in form to other suggestions like full `typedef` support in
|
tuples, generics, etc ... This is likely to be similar in form to other suggestions like full `typedef` support in
|
||||||
the language.
|
the language.
|
||||||
|
@ -367,12 +371,14 @@ the language.
|
||||||
## Future Considerations
|
## Future Considerations
|
||||||
|
|
||||||
### static local functions
|
### static local functions
|
||||||
|
|
||||||
This refers to [the proposal](https://github.com/dotnet/csharplang/issues/1565) to allow the
|
This refers to [the proposal](https://github.com/dotnet/csharplang/issues/1565) to allow the
|
||||||
`static` modifier on local functions. Such a function would be guaranteed to be emitted as
|
`static` modifier on local functions. Such a function would be guaranteed to be emitted as
|
||||||
`static` and with the exact signature specified in source code. Such a function should be a valid
|
`static` and with the exact signature specified in source code. Such a function should be a valid
|
||||||
argument to `&` as it contains none of the problems local functions have today
|
argument to `&` as it contains none of the problems local functions have today
|
||||||
|
|
||||||
### static delegates
|
### static delegates
|
||||||
|
|
||||||
This refers to [the proposal](https://github.com/dotnet/csharplang/issues/302) to allow for the declaration of
|
This refers to [the proposal](https://github.com/dotnet/csharplang/issues/302) to allow for the declaration of
|
||||||
`delegate` types which can only refer to `static` members. The advantage being that such `delegate` instances can be
|
`delegate` types which can only refer to `static` members. The advantage being that such `delegate` instances can be
|
||||||
allocation free and better in performance sensitive scenarios.
|
allocation free and better in performance sensitive scenarios.
|
||||||
|
@ -390,5 +396,5 @@ That means developers essentially have to decide between the following trade off
|
||||||
|
|
||||||
1. Safety in the face of assembly unloading: this requires allocations and hence `delegate` is already a sufficient
|
1. Safety in the face of assembly unloading: this requires allocations and hence `delegate` is already a sufficient
|
||||||
option.
|
option.
|
||||||
1. No safety in face of assembly unloading: use a `func*`. This can be wrapped in a `struct` to allow usage outside
|
1. No safety in face of assembly unloading: use a `delegate*`. This can be wrapped in a `struct` to allow usage outside
|
||||||
an `unsafe` context in the rest of the code.
|
an `unsafe` context in the rest of the code.
|
||||||
|
|
Loading…
Reference in a new issue