Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the static-delegate proposal. #126

Merged
merged 3 commits into from
Mar 16, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions proposals/static-delegates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Static Delegates

* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started

## Summary
[summary]: #summary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this mark down trick doing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It allows you to link to the sub-section of the document (this is based on the proposal-template.md).


Provide a general-purpose, lightweight callback capability to the C# language.

## Motivation
[motivation]: #motivation

Today, users have the ability to create callbacks using the `System.Delegate` type. However, these are fairly heavyweight (such as requiring a heap allocation and always having handling for chaining callbacks together).

Additionally, `System.Delegate` does not provide the best interop with unmanaged function pointers, namely due being non-blittable and requiring marshalling anytime it crosses the managed/unmanaged boundary.

With a few minor tweaks, we could provide a new type of delegate that is lightweight, general-purpose, and interops well with native code.

## Detailed design
[design]: #detailed-design

One would declare a static delegate via the following:

```C#
static delegate int Func()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bikeshed: static delegate could be confusing such that it can only refer to static members (in fact I'm already confused and not sure if that's the case), I'd suggest struct delegate though that's probably your last concern. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alrz, static delegates can only refer to static methods. This is because delegates which refer to instance method also require you to carry a reference to the this object and that makes them inherently non-blittable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, maybe value delegate to better match the naming convention for other types we have done similar things (ValueTuple, ValueTask, etc...), although I don't much care what the name is, provided I can use the feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer static delegate over value delegate.

The Value pattern is used today for types which are being made a struct type. The Value prefix is given to distinguish them from the well know class types. If there were a well known base type for this feature then I agree that calling it ValueDelegate would make sense. That's not the case though here, there is in fact no common base type to refer to.

In this case it's a delegate that can only refer to static members. There are existing examples in the language of using static to modify existing types such that it can only contain static content: static class. The static delegate notation is just feeding into this existing usage.

Copy link
Member

@alrz alrz Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about open delegates? Delegate.CreateDelegate would probably not work with this. Will there an API to create an open static delegate that directly points to a member of the input parameter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confident that this document correctly reflects the current (very preliminary) state of the proposal. We can discuss possible changes to it in the linked issue.

```

One could additionally attribute the declaration with something similar to `System.Runtime.InteropServices.UnmanagedFunctionPointer` so that the calling convention, string marshalling, and set last error behavior can be controlled. NOTE: Using `System.Runtime.InteropServices.UnmanagedFunctionPointer` itself will not work, as it is only usable on Delegates.

The declaration would get translated into an internal representation by the compiler that is similar to the following

```C#
struct <Func>e__StaticDelegate
{
IntPtr pFunction;

static int WellKnownCompilerName();
}
```

That is to say, it is internally represented by a struct that has a single member of type `IntPtr` (such a struct is blittable and does not incur any heap allocations):
* The member contains the address of the function that is to be the callback.
* The type declares a method matching the method signature of the callback.
* The name of the struct should not be user-constructable (as we do with other internally generated backing structures).
* For example: fixed size buffers generate a struct with a name in the format of `<name>e__FixedBuffer` (`<` and `>` are part of the identifier and make the identifier not constructable in C#, but still useable in IL).
* The name of the method declaration should be a well known name used across all static delegate types (this allows the compiler to know the name to look for when determining the signature).

The value of the static delegate can only be bound to a static method that matches the signature of the callback.

Chaining callbacks together is not supported.

Invocation of the callback would be implemented by the `calli` instruction.

## Drawbacks
[drawbacks]: #drawbacks

Static Delegates would not work with existing APIs that use regular delegates (one would need to wrap said static delegate in a regular delegate of the same signature).
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably worth noting that System.Delegate is represented (internally) as a set of object and IntPtr fields (http://source.dot.net/#System.Private.CoreLib/src/System/Delegate.cs). It may be possible to provide a one way conversion from static delegate to System.Delegate (this could possibly be bidirectional if we can validate the Delegate conforms to the requirements of a static delegate).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This incompatibility could be addressed though. The language could allow an implicit conversion from any static delegate to a normal delegate with compatible signatures.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, which is why I called it out here. I will add a note to the proposal indicating that we could workaround this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note added.

* Given that `System.Delegate` is represented internally as a set of `object` and `IntPtr` fields (http://source.dot.net/#System.Private.CoreLib/src/System/Delegate.cs), it would be possible to allow implicit conversion of a static delegate to a `System.Delegate` that has a matching method signature. It would also be possible to provide an explicit conversion in the opposite direction, provided the `System.Delegate` conformed to all the requirements of being a static delegate.

Additional work would be needed to make Static Delegate readily usable in the core framework.

## Alternatives
[alternatives]: #alternatives

TBD
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C# does not currently provide any mechanism to expose the calli instruction. So, I believe the only alternative would be to implement the required functionality in pure IL.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.


## Unresolved questions
[unresolved]: #unresolved-questions

What parts of the design are still TBD?

## Design meetings

Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to.