Skip to content

Issues/Questions on the V9 proposal for function pointers #28782

Description

@RexJaeschke

Using this proposal, I’ve been writing a formal spec for this feature to add to the C# standard, and have the following comments:

Comment 1: “Syntactically incorrect code examples”

In “Function pointers,” we have the following code fragments:

delegate* managed<int, int>;
delegate* unmanaged<int, int>;
delegate* unmanaged[Cdecl] <int, int>;
delegate* unmanaged[Stdcall, SuppressGCTransition] <int, int>;

As written, each is a type followed by a semicolon. Either remove the semicolons or add a distinct identifier before each to make them valid parameter or variable declarations.

The same thing occurs in “Function pointer syntax”, in the following:

delegate*<string, int>;
delegate*<delegate*<string, int>, delegate*<string, int>>;
delegate* managed<string, int>;
delegate*<delegate* managed<string, int>, delegate*<string, int>>;

Comment 2: “Add extra restriction”

In “Function pointers,” there is a list of Restrictions. Add to that,

  • A delegate* cannot be dereferenced.

W.r.t the unary * operator, I plan to add the following Note to the C# standard:

Note: In C and C++, calling a function via a function pointer fp can be done using either (*fp)() or fp(). C# supports the latter only, as a function pointer cannot be dereferenced explicitly. end note

BTW, attempting to dereference a properly initialized function pointer fp using (*fp)() results in VS 16.11.2 telling me, “CS0193: the * or -> operator must be applied to a pointer.” That was [almost] correct before function pointers were added, but it should now say “data pointer” instead of “pointer,” or “* and -> operator cannot be applied to a function pointer*.” [Re the current message, I see that attempting to dereference a void pointer results in CS0242, a different message, even though a void pointer is a pointer.]

Note that my use of “data pointer” excludes void*. As I’m adding function pointer support to the standard, I’m breaking the pointer_type grammar rule into three subrules, as follows, so I can distinguish between data and void pointers:

pointer_type
    : dataptr_type
    | funcptr_type
    | voidptr_type
    ;

Comment 3: “Grammar rule name typo”

In “Function pointer syntax,” the name of the grammar rule funptr_parameter_list is misspelled. The reference to that rule (and all other function-pointer rule names) use a “fun[c]” prefix instead, so this should be funcptr_parameter_list.

Comment 4: “Unnecessary new grammar rule”

In “Function pointer syntax,” rule funcptr_parameter_modifier is defined, as follows, and is referenced:

funcptr_parameter_modifier
    : 'ref'
    | 'out'
    | 'in'
;

The existing rule parameter_mode_modifier has that exact definition, and can be used here instead.

Comment 5: “Function pointer comparisons”

When I wrote code to compare two function pointers, I go the warning “CS8909 Comparison of function pointers might yield an unexpected result, since [multiple] pointers to the same function may be distinct.” A Google search led me to an issue on stackoverflow, which, in turn, led me to Roslyn Issue #48919, “Warn on pointer comparison between function pointers.”

What should I say in the C# standard w.r.t the reliability of the six kinds of comparisons of two function pointers? Is it always unreliable? Or is it only so with pointers to managed methods? To unmanaged methods?

Comment 6: “Question regarding method signature and calling convention combination”

In “Allow address-of to target methods,” the proposal states, “Method groups will now be allowed as arguments to an address-of expression. The type of such an expression will be a delegate* which has the equivalent signature of the target method and a managed calling convention:.”

I’m confused by the presence of managed. How does one initialize an unmanaged function pointer to point to an unmanaged method? I’m thinking specifically of calling a function written in C using the Cdecl convention.

Unless I’m missing something, my guess is that managed should be removed.

Comment 7: “Spurious ; in example”

In “Allow address-of to target methods,” we have

unsafe class Util {
    public static void Log() { }
    public static void Log(string p1) { }
    public static void Log(int i) { };

The one trailing ; should be removed.

Comment 8: “Function pointer invocations”

§11.7.8, “Invocation expressions” of the C# standard covers calls to methods by name or via a delegate. I expected this proposal to contain text augmenting that to cover calls to methods via a function pointer. And while there are some suggestions about that, mostly in the context of overloaded method selection and such during conversion with the & operator, I don’t see anything I can use to create a new section “Function pointer invocations” for the Unsafe chapter.

My current thinking is that in this new section I’d say something like the following: “A function pointer invocation is handled like a method invocation (§11.7.8.2), but with primary_expression being a function pointer rather than a method.” Is that completely true, or do I need to add/remove steps as well? If so, depending on how much modification of those steps is necessary, I might have to clone text from the method-invocation section rather than punt to it.

At the end of the proposal section, “Operators on Function Pointer Types,” the following should be added to the list of operations permitted on a function type:

  • The invocation-expression operator, (), may be used to call the method being pointed to (§11.7.8.1).

The one thing applicable to function pointer invocation that does not apply to method invocation, is the possibility of primary_expression being null. My test shows that attempting to call through a null-valued function pointer results in an exception of type System.AccessViolationException. My guess is the standard should say that “the nature of the failure is implementation-defined” rather than guarantee the actual type thrown. (We already say, “The effect of applying the unary * operator to a null [data] pointer is implementation-defined. In particular, there is no guarantee that this operation throws a System.NullReferenceException.)

Any light you can shed on this would be much appreciated.

Comment 9: “UnmanagedCallersOnlyAttribute”

This part of the proposal looks like implementation detail. As such, I don’t mention it in my C# standard proposal. Is that reasonable?

The MS proposal doesn’t show any examples of initializing an unmanaged function pointer using &. I’m guessing it might go something like this:

[UnmanagedCallersOnlyAttribute(CallConvs = new[] { typeof(CallConvCdecl) })]
static extern void Cfunction(int value);

delegate* unmanaged[Cdecl]<int, void> fp = &Cfunction;

Am I on the right track?


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions