[Version 9.0] Feature support for function pointers - #1459
BillWagner wants to merge 4 commits into
Conversation
8444d03 to
62a3482
Compare
fad32de to
3ea1ed0
Compare
3ea1ed0 to
b9681d6
Compare
b9681d6 to
2aacdf4
Compare
| - `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`. |
There was a problem hiding this comment.
For later polish: we should check whether we've got a better term than "refness".
There was a problem hiding this comment.
I don't think we do. I think we should.
| - 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: |
There was a problem hiding this comment.
I don't understand these bullets... Perhaps "For the return type V1" and "For the parameter types V2..VK"?
| - 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`: |
There was a problem hiding this comment.
Same as earlier, I think we can do better than this.
| - 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*`. |
There was a problem hiding this comment.
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.
| <!-- | ||
| [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 | ||
| --> |
There was a problem hiding this comment.
We'll want to remove this TODO - it's not clear to me whether the text additions later are sufficient.
| > `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 |
There was a problem hiding this comment.
Maybe add an example with parameter types and a return type.
| 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)). |
There was a problem hiding this comment.
Again, this is text which has been moved.
| ; | ||
|
|
||
| funcptr_return_type | ||
| : ref_kind? return_type |
There was a problem hiding this comment.
This would appear to allow ref void as a return type. We probably need to prohibit that.
| > 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. |
There was a problem hiding this comment.
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?
|
|
||
| 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*. |
There was a problem hiding this comment.
It's unclear what's meant by multiple calling conventions being specified - are they alternatives, additional constraints, or is that implementation-specific?
|
|
||
| - 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)). |
There was a problem hiding this comment.
Not sure whether invocation_expression should be in italics, code, or should just not have the underscore.
| > } | ||
| > ``` | ||
| > | ||
| > both cases use the identifier-list grammar rule. *end example* |
There was a problem hiding this comment.
I don't think there is such a grammar rule. Not sure what's intended here.
|
|
||
| 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: |
There was a problem hiding this comment.
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"?
|
|
||
| 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`. |
There was a problem hiding this comment.
Not sure whether this has been moved or just removed - or why...
| ``` | ||
|
|
||
| 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`. |
There was a problem hiding this comment.
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.)
|
|
||
| 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`. |
There was a problem hiding this comment.
| 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`. |
|
|
||
| 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`. |
There was a problem hiding this comment.
| 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' |
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
This specification doesn't actually provide any semantics for Stdcall etc as far as I can see...
| ### §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)). |
There was a problem hiding this comment.
Is a pointer actually a variable? I'd expect it to be a value. This may be academic...
| ``` | ||
|
|
||
| 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. |
There was a problem hiding this comment.
"the type of the variable" feels like it misses out function pointers. Not sure what we'll want to do with that.
jskeet
left a comment
There was a problem hiding this comment.
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.
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>
77557e4 to
415828d
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: bce5e82a-89fd-4655-bc08-6d6ba97f0cc6
All commits from #984 have been added.
PR #984 has three conversations that haven't been addressed: