[Version 9.0] Feature support for function pointers - #1459
Conversation
8444d03 to
62a3482
Compare
fad32de to
3ea1ed0
Compare
3ea1ed0 to
b9681d6
Compare
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
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.
| - It is an error for that method to be in a generic type. | ||
| - It is an error to convert that method to a delegate type. | ||
|
|
||
| For a function pointer invocation, the *primary_expression* of the *invocation_expression* shall be a value of a *funcptr_type*. Furthermore, considering the method being pointed to to be a function member with the same parameter list as the *funcptr_type*, the *funcptr_type* shall be applicable ([§12.6.4.2](expressions.md#12642-applicable-function-member)) with respect to the *argument_list* of the *invocation_expression*. |
There was a problem hiding this comment.
Let's try to avoid the stuttering in this part:
Furthermore, considering the method being pointed to to be a function member with the same parameter list as the funcptr_type
|
|
||
| - `F` is evaluated. If this evaluation causes an exception, no further steps are executed. | ||
| - The argument list `A` is evaluated. If this evaluation causes an exception, no further steps are executed. | ||
| - The value of `F` is checked to be valid. If that value is `null`, an implementation-defined exception is thrown, and no further steps are executed. |
There was a problem hiding this comment.
Does "valid" just mean "non-null"? If so, let's change it to say that - otherwise, let's add more detail about what "valid" means.
|
|
||
| The intuitive rule for mixing pointers and references is that referents of references (objects) are permitted to contain pointers, but referents of pointers are not permitted to contain references. | ||
|
|
||
| For a given implementation, all pointer types shall have the same size and representation. A null pointer value shall be represented by all-bits-zero. |
There was a problem hiding this comment.
Do we need this to be in the spec?
|
|
||
| For a given implementation, all pointer types shall have the same size and representation. A null pointer value shall be represented by all-bits-zero. | ||
|
|
||
| Pointer types are a separate category of types. Unlike reference types and value types, pointer types do not inherit from `object` and no conversions exist between pointer types and `object`. In particular, boxing and unboxing ([§8.3.13](types.md#8313-boxing-and-unboxing)) are not supported for pointers. However, conversions are permitted between different pointer types and between pointer types and the integral types. This is described in [§24.5](unsafe-code.md#245-pointer-conversions). |
There was a problem hiding this comment.
Note: this is moved text rather than new text (including the restrictions and permissions below).
| ; | ||
| ``` | ||
|
|
||
| A *dataptr_type* is written as an *unmanaged_type* ([§8.8](types.md#88-unmanaged-types)), *funcptr_type*, or *voidptr_type*, followed by one or more `*` tokens. |
There was a problem hiding this comment.
Hmm... that doesn't seem to be what's described above. (value_type instead of unmanaged_type)
|
|
||
| ### §data-pointers Data pointers | ||
|
|
||
| A ***data pointer*** is a pointer capable of containing the address of a variable having *value_type* ([§8.3.1](types.md#831-general)), *funcptr_type* (§function-pointers), or *voidptr_type* (§void-pointers). |
There was a problem hiding this comment.
Not sure what "having" means here.
| > | ||
| > *end example* | ||
|
|
||
| ### §data-pointers Data pointers |
There was a problem hiding this comment.
I'd expected to see "data pointers" as separate from "function pointers" here, but it looks like it includes function pointers...
There was a problem hiding this comment.
(Or is it only "pointers to function pointers"? Maybe...)
There was a problem hiding this comment.
I think it's probably worth clarifying this...
| > `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.)
Co-authored-by: Jon Skeet <skeet@pobox.com>
All commits from #984 have been added.
PR #984 has three conversations that haven't been addressed: