Skip to content

[Version 9.0] Feature support for function pointers - #1459

Draft
BillWagner wants to merge 4 commits into
draft-v9from
v9-function-pointers
Draft

BillWagner wants to merge 4 commits into
draft-v9from
v9-function-pointers

Conversation

@BillWagner

@BillWagner BillWagner commented Nov 10, 2025 •

Copy link
Copy Markdown
Member

@BillWagner
BillWagner changed the base branch from draft-v8 to draft-v9 November 10, 2025 20:57
@BillWagner BillWagner closed this Nov 10, 2025
@BillWagner BillWagner reopened this Nov 10, 2025
@BillWagner
BillWagner force-pushed the v9-function-pointers branch 2 times, most recently from 8444d03 to 62a3482 Compare November 10, 2025 21:23
@RexJaeschke RexJaeschke added this to the C# 9.0 milestone Nov 12, 2025
@RexJaeschke RexJaeschke added type: feature This issue describes a new feature Review: pending Proposal is available for review labels Nov 12, 2025
@RexJaeschke RexJaeschke added Review: in progress at least one person is reviewing this and removed Review: pending Proposal is available for review labels Nov 30, 2025
@BillWagner
BillWagner force-pushed the v9-function-pointers branch from fad32de to 3ea1ed0 Compare April 10, 2026 18:32
@BillWagner
BillWagner force-pushed the v9-function-pointers branch from 3ea1ed0 to b9681d6 Compare May 12, 2026 19:57
@BillWagner
BillWagner force-pushed the v9-function-pointers branch from b9681d6 to 2aacdf4 Compare July 24, 2026 19:50
@jskeet jskeet self-assigned this Aug 10, 2026
Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md Outdated
- `V` is an array type `V₁[...]`and `U` is an array type `U₁[...]`of the same rank
- `V` is one of `IEnumerable<V₁>`, `ICollection<V₁>`, `IReadOnlyList<V₁>>`, `IReadOnlyCollection<V₁>` or `IList<V₁>` and `U` is a single-dimensional array type `U₁[]`
- `V` is a constructed `class`, `struct`, `interface` or `delegate` type `C<V₁...Vₑ>` and there is a unique type `C<U₁...Uₑ>` such that `U` (or, if `U` is a type `parameter`, its effective base class or any member of its effective interface set) is identical to, `inherits` from (directly or indirectly), or implements (directly or indirectly) `C<U₁...Uₑ>`.
- `V` is a function pointer type (§function-pointers) `delegate*<V2..Vk, V1>` and there is a function pointer type `delegate*<U2..Uk, U1>` such that `U` is identical to `delegate*<U2..Uk, U1>`, and the calling convention of `V` is identical to `U`, and the refness of `Vi` is identical to `Ui`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For later polish: we should check whether we've got a better term than "refness".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think we do. I think we should.

Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md
- If it is contravariant then an *upper-bound inference* is made.
- If it is invariant then an *exact inference* is made.
- Otherwise, if `V` is `delegate*<V2..Vk, V1>` then inference depends on the i-th parameter of `delegate*<V2..Vk, V1>`:
- If V1:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't understand these bullets... Perhaps "For the return type V1" and "For the parameter types V2..VK"?

Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md
- If it is contravariant then a *lower-bound inference* is made.
- If it is invariant then an *exact inference* is made.
- Otherwise, if `U` is `delegate*<U2..Uk, U1>` then inference depends on the i-th parameter of `delegate*<U2..Uk, U1>`:
- If `U1`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as earlier, I think we can do better than this.

Comment thread standard/expressions.md
- If for at least one parameter `Mᵥ` uses the ***better parameter-passing choice*** ([§12.6.4.4](expressions.md#12644-better-parameter-passing-mode)) than the corresponding parameter in `Mₓ` and none of the parameters in `Mₓ` use the better parameter-passing choice than `Mᵥ`, `Mᵥ` is better than `Mₓ`.
- Otherwise, no function member is better.

A `delegate*` is more specific than `void*`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This feels a little out of place at the moment - is this for parameters, return types, anything else? (If it's parameters, then it should be in the "if Mv has more specific parameter types" bit.

Comment thread standard/expressions.md Outdated
Comment thread standard/expressions.md
Comment on lines 1993 to 1996
<!--
[ToDo] C#9’s function pointers are also excluded, as the following restriction is stated
in terms of what is included the text will probably be fine but this will need to be confirmed
-->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We'll want to remove this TODO - it's not clear to me whether the text additions later are sufficient.

Comment thread standard/unsafe-code.md
> `int*[]` | Single-dimensional array of pointers to `int`
> `void*` | Pointer to unknown type
> `char**` | Pointer to pointer to `char`
> `delegate*<void>*` | Pointer to a pointer to a static method having no parameters and a `void` return type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe add an example with parameter types and a return type.

Comment thread standard/unsafe-code.md
In an unsafe context, several constructs are available for operating on data pointers:

> *Example*: When given a pointer to a contiguous sequence of `int`s, that sequence’s element count, and some other `int` value, the following method returns the address of that value in that sequence, if a match occurs; otherwise it returns `null`:
- The unary `*` operator may be used to perform pointer indirection ([§24.6.2](unsafe-code.md#2462-pointer-indirection)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Again, this is text which has been moved.

Comment thread standard/unsafe-code.md
;

funcptr_return_type
: ref_kind? return_type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This would appear to allow ref void as a return type. We probably need to prohibit that.

Comment thread standard/unsafe-code.md
> Given that the function pointers in the array point to methods with no parameters, `&Log` takes the address of the `Log` method having no parameters. *end example*

In an unsafe context, several constructs are available for operating on pointers:
*unmanaged_calling_convention* supports a small number of predefined conventions (`Cdecl`, `Stdcall`, `Thiscall`, and `Fastcall`, all of which are contextual keywords), which may be used standalone or as an *identifier* in an *unmanaged_calling_convention* identifier list. Other implementation-defined conventions are permitted, and multiple conventions can be combined by using an *identifier* list, possibly containing one or more of these predefined conventions. Lookup and processing for identifiers in this list is done in an implementation-defined manner.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's probably worth discussing in a meeting whether we really need the predefined conventions to be included in the standard. If we have to make sure that implementation-defined conventions "work" (in terms of still being contextual keywords etc) then do we actually need these?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(This echoes Kalle's comment in #984.)

Comment thread standard/unsafe-code.md

If no *calling_convention_specifier* is provided, the default is `managed`, which results in the execution environment’s default mechanism being used. Specific unmanaged conventions can be specified using *unmanaged_calling_convention* whose tokens are mapped to implementation-defined names having implementation-defined semantics. The set of valid combinations of these tokens is implementation-defined.

> *Note*: The *calling_convention_specifier* allows a potentially more efficient calling mechanism to be chosen, or for methods written in languages other than C# to be called. *end note*.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It's unclear what's meant by multiple calling conventions being specified - are they alternatives, additional constraints, or is that implementation-specific?

Comment thread standard/unsafe-code.md Outdated
Comment thread standard/unsafe-code.md

- The `&` operator may be used to obtain the address of a static method ([§24.6.5](unsafe-code.md#2465-the-address-of-operator))
- The `==`, `!=`, `<`, `>`, `<=`, and `=>` operators may be used to compare pointers ([§24.6.8](unsafe-code.md#2468-pointer-comparison)).
- The invocation_expression operator, `()`, may be used to call the method being pointed to ([§12.8.9.1](expressions.md#12891-general)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure whether invocation_expression should be in italics, code, or should just not have the underscore.

Comment thread standard/unsafe-code.md
> }
> ```
>
> both cases use the identifier-list grammar rule. *end example*

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think there is such a grammar rule. Not sure what's intended here.

Comment thread standard/unsafe-code.md Outdated
Comment thread standard/unsafe-code.md

A *voidptr_type* is written as the keyword `void` followed by thye `*` token.

> *Example*: Some examples of void-pointer types are given in the table below:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure whether the array is really a void pointer type... it's not itself unknown, and it doesn't follow the rules above. Maybe this should be "some examples of the use of void pointers" are given in the table below"?

Comment thread standard/unsafe-code.md Outdated
Comment thread standard/unsafe-code.md Outdated
Comment thread standard/unsafe-code.md

When recognising a *primary_expression* if both the *element_access* and *pointer_element_access* ([§24.6.4](unsafe-code.md#2464-pointer-element-access)) alternatives are applicable then the latter shall be chosen if the embedded *primary_expression* is of pointer type ([§24.3](unsafe-code.md#243-pointer-types)).

In a pointer element access of the form `P[E]`, `P` shall be an expression of a pointer type other than `void*`, and `E` shall be an expression that can be implicitly converted to `int`, `uint`, `long`, or `ulong`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure whether this has been moved or just removed - or why...

Comment thread standard/unsafe-code.md Outdated
Comment thread standard/unsafe-code.md Outdated
Comment thread standard/unsafe-code.md
```

Given an expression `P` of a pointer type `T*` and an expression `N` of type `int`, `uint`, `long`, or `ulong`, the expressions `P + N` and `N + P` compute the pointer value of type `T*` that results from adding `N * sizeof(T)` to the address given by `P`. Likewise, the expression `P – N` computes the pointer value of type `T*` that results from subtracting `N * sizeof(T)` from the address given by `P`.
Given an expression `P` of a data pointer type `T*` and an expression `N` of type `int`, `uint`, `long`, or `ulong`, the expressions `P + N` and `N + P` compute the pointer value of type `T*` that results from adding `N * sizeof(T)` to the address given by `P`. Likewise, the expression `P – N` computes the pointer value of type `T*` that results from subtracting `N * sizeof(T)` from the address given by `P`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm tempted to change these to p and n to be more consistent, given that they're not types. Feedback welcome. (Ditto P and Q below.)

Comment thread standard/unsafe-code.md Outdated
Comment thread standard/expressions.md

If `E` is a method group or implicitly typed anonymous function and `T` is a delegate type or expression tree type then all the parameter types of `T` are *input types of* `E` *with type* `T`.

If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then all the parameter types of `T` are input types of `E` with type `T`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then all the parameter types of `T` are input types of `E` with type `T`.
If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then all the parameter types of `T` are *input types of* `E` *with type* `T`.

Comment thread standard/expressions.md

If `E` is a method group or an anonymous function and `T` is a delegate type or expression tree type then the return type of `T` is an *output type of* `E` *with type* `T`.

If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then the return type of `T` is an output type of `E` with type `T`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then the return type of `T` is an output type of `E` with type `T`.
If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then the return type of `T` is an *output type of* `E` *with type* `T`.

| 'let' | 'nameof' | 'notnull' | 'on' | 'orderby'
| 'partial' | 'remove' | 'select' | 'set' | 'unmanaged'
| 'value' | 'var' | 'when' | 'where' | 'yield'
: 'add' | 'alias' | 'ascending' | 'async' | 'await'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note: there's a later comment suggesting that we might not want Fastcall, Stdcall, Thiscall etc... in which case we'd need to edit this bit.


1. The behavior of the enclosing async function when an awaiter’s implementation of the interface methods `INotifyCompletion.OnCompleted` and `ICriticalNotifyCompletion.UnsafeOnCompleted` does not cause the resumption delegate to be invoked at most once ([§12.9.9.4](expressions.md#12994-run-time-evaluation-of-await-expressions)).
1. Passing pointers as reference or output parameters ([§24.3](unsafe-code.md#243-pointer-types)).
1. Passing pointers as `ref` or `out` parameters (§data-pointers).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this actually be "passing data pointers" or does it also apply to function pointers?

1. The impact of thread termination when a thread has no handler for an exception, and the thread is itself terminated. ([§13.10.6](statements.md#13106-the-throw-statement))
1. The mechanism by which linkage to an external method is achieved. ([§15.6.8](classes.md#1568-external-methods))
1. The impact of thread termination when no matching `catch` clause is found for an exception and the code that initially started that thread is reached. ([§22.4](exceptions.md#224-how-exceptions-are-handled)).
1. The token name mapping and semantics of unmanaged calling conventions beyond those required by this specification, and the set of valid combinations of those tokens (§function-pointers).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This specification doesn't actually provide any semantics for Stdcall etc as far as I can see...

Comment thread standard/unsafe-code.md
### §pointer-types-general General

A *pointer_type* is written as a *value_type* that is an unmanaged type ([§8.8](types.md#88-unmanaged-types)) or the keyword `void`, followed by one or more `*` tokens:
A ***pointer*** is a variable that is capable of containing the address of a variable or static method, referred to as that pointer's target. A pointer with value `null` is a ***null pointer***, and does not currently point to a variable or static method. The act of attempting to access the target of a pointer is called ***dereferencing*** ([§24.6.2](unsafe-code.md#2462-pointer-indirection) and [§24.6.4](unsafe-code.md#2464-pointer-element-access)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is a pointer actually a variable? I'd expect it to be a value. This may be academic...

Comment thread standard/unsafe-code.md
```

The type specified before the `*` in a pointer type is called the ***referent type*** of the pointer type. It represents the type of the variable to which a value of the pointer type points.
The type of the target of a pointer type is called the ***referent type*** of the pointer type. It represents the type of the variable to which a value of the pointer type points.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"the type of the variable" feels like it misses out function pointers. Not sure what we'll want to do with that.

@jskeet jskeet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is probably ready for other folks to have a look at, but as noted I've got several comments. I'm not marking it as "meeting: discuss" as I don't think we should focus on it for September, but possibly in the October meeting.

I think the overall shape is probably okay, but there are some important decisions to make (e.g. calling conventions) and various nitpicks and tweaks required.

RexJaeschke and others added 3 commits September 18, 2026 15:55
Add support for function pointers

Add support for function pointers

Add support for function pointers

fix md and links

fix md

Add support for function pointers

Add support for function pointers

Add support for function pointers

disable undefined output

Update unsafe-code.md

fix formatting

fix link

fix link

fix link

Overhaul pointer_type grammar and description

fix formatting

fix grammar rule name spelling

fix merge issues

fix link
Co-authored-by: Jon Skeet <skeet@pobox.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: bce5e82a-89fd-4655-bc08-6d6ba97f0cc6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review: in progress at least one person is reviewing this type: feature This issue describes a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants