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

Adding the static-delegate proposal. #126

merged 3 commits into from
Mar 16, 2017

Conversation

tannergooding
Copy link
Member

FYI. @MadsTorgersen, @gafter

This is the official proposal for #80, which @jaredpar has agreed to champion.

## 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.

## Drawbacks
[drawbacks]: #drawbacks

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.

I can't really think of any drawbacks here. The design described above should be completely verifiable and relatively simple to implement.

Copy link
Member Author

Choose a reason for hiding this comment

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

I thought of some drawbacks last night, mostly due to the comment by @alrz 😄


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. Additionally, the type declares a method matching the method signature of the callback.

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

@tannergooding tannergooding Feb 16, 2017

Choose a reason for hiding this comment

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

For better interop with unmanaged code, it might be desirable to allow assigning the value of a static delegate to that of an IntPtr (in an unsafe context only given that it is unverifiable).

However, given that the static delegate type is itself blittable, the only scenario this would improve is if you had an unmanaged function that returned a void* instead of a typed function pointer (I can't think of any APIs off the top of my head that do this and I wouldn't think this would be common practice anyways).

As such, I left that off the proposal.

@alrz
Copy link
Member

alrz commented Feb 16, 2017

So we will have the whole set of new delegate types StaticAction<..> etc?

@tannergooding
Copy link
Member Author

@alrz, I believe so. Although that would be part of a coordinated effort with the framework team (for betterness), rather than part of the proposed language spec/

## 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.

* [ ] 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).

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

```C#
struct <name>
Copy link
Member

Choose a reason for hiding this comment

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

The <name> here should be Func.

Copy link
Member Author

Choose a reason for hiding this comment

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

I put a placeholder here because I think just Func isn't enough, it would need to be a non-user constructable identifier as we do with other backing structs. For example, with fixed size buffers we do <name>e__FixedBuffer (the < and > are not placeholders but part of the actual identifier), so in this case we would do something like <Func>e__StaticDelegate (or something similar, I would imagine).

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed the placeholder and provided a text explanation/example.

{
IntPtr pFunction;

static 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.

The name here should be a standard name used across all static function types. That way the compiler knows what name to look for when determining the signature.

Not sure if that's going too far or not for the language spec though as it's a bit of implementation detail of the compiler.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure either. @MadsTorgersen or @gafter, how should we represent placeholder data like 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.

I updated this to be WellKnownCompilerName for the time being and added a text description indicating as such.


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


Copy link
Member

Choose a reason for hiding this comment

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

double blank line.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.

## 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

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.

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.

@tannergooding
Copy link
Member Author

@jaredpar, I believe (since you are championing this) the proposal needs your explicit approval before it can be merged. Let me know if you think any further modifications or clarifications are necessary.

@jaredpar
Copy link
Member

CC @dotnet/csharplangdesign

@jaredpar
Copy link
Member

@tannergooding one other aspect I think we need to make note of somewhere: this will require some run time changes in the following areas:

  • Verification: the verifier has special knowledge of the manner in which normal delegates are created. Essentially to ensure the method token being passed to the constructor is indeed a valid signature. This same process needs to be applied to static delegates.
  • Code unloading: for environments where code unloading is supported (AppDomains for example) there likely needs to be special knowledge of static delegates. They represent a new type which can essentially prevent DLLs from being unloaded.

I'm unsure if the proposal needs to go that deep here. Will defer to @gafter if that is appropriate or not here.

@tannergooding
Copy link
Member Author

@jaredpar is that actually the case though?

For any calli instruction, the following correctness & verification exists:
image

In order to assign the value of a static delegate, one would additionally need to either have the declaration come from native code (unverifiable) or one would need to use the ldftn instruction, which has the following correctness & verification:
image

My understanding of that is that, for verifiable code, the verifier should already be able to handle combinations of ldftn and calli, or is that not the case?

@tannergooding
Copy link
Member Author

@jaredpar, @gafter. Is there anything else that needs to be figured out here to get the proposal merged (it can certainly be updated further, post-merge, if need be)?

@gafter
Copy link
Member

gafter commented Mar 16, 2017

I think this is fine to merge (@jaredpar). We can refine it later as needed.

@tannergooding tannergooding merged commit c5de26f into dotnet:master Mar 16, 2017
@tannergooding
Copy link
Member Author

#302

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants