-
Notifications
You must be signed in to change notification settings - Fork 99
[Version 9.0] Feature support for function pointers #1459
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
base: draft-v9
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,15 @@ | ||
| # 12 Expressions | ||
|
|
||
| ## 12.1 General | ||
|
Check warning on line 3 in standard/expressions.md
|
||
|
|
||
| An expression is a sequence of operators and operands. This clause defines the syntax, order of evaluation of operands and operators, and meaning of expressions. | ||
|
|
||
|
Check warning on line 6 in standard/expressions.md
|
||
| ## 12.2 Expression classifications | ||
|
|
||
| ### 12.2.1 General | ||
|
|
||
| The result of an expression is classified as one of the following: | ||
|
|
||
| - A value. Every value has an associated type. | ||
| - A variable. Unless otherwise specified, a variable is explicitly typed and has an associated type, namely the declared type of the variable. An implicitly typed variable has no associated type. | ||
| - A null literal. An expression with this classification can be implicitly converted to a reference type or nullable value type. | ||
|
|
@@ -782,10 +782,18 @@ | |
|
|
||
| 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`. | ||
|
|
||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
|
|
||
| #### 12.6.3.5 Output types | ||
|
|
||
| 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`. | ||
|
|
||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
|
|
||
| #### 12.6.3.6 Dependence | ||
|
|
||
| An *unfixed* type variable `Xᵢ` *depends directly on* an *unfixed* type variable `Xₑ` if for some argument `Eᵥ` with type `Tᵥ` `Xₑ` occurs in an *input type* of `Eᵥ` with type `Tᵥ` and `Xᵢ` occurs in an *output type* of `Eᵥ` with type `Tᵥ`. | ||
|
|
@@ -811,6 +819,8 @@ | |
| - If `E` is a tuple literal with arity `N` and elements `Eᵢ`, and `T` is a tuple type with arity `N` with corresponding element types `Tₑ` or `T` is a nullable value type `T0?` and `T0` is a tuple type with arity `N` that has a corresponding element type `Tₑ`, then for each `Eᵢ` an output type inference is made from `Eᵢ` to `Tₑ`. | ||
| - If `E` is an anonymous function with inferred return type `U` ([§12.6.3.14](expressions.md#126314-inferred-return-type)) and `T` is a delegate type or expression tree type with return type `Tₓ`, then a *lower-bound inference* ([§12.6.3.11](expressions.md#126311-lower-bound-inferences)) is made *from* `U` *to* `Tₓ`. | ||
| - Otherwise, if `E` is a method group and `T` is a delegate type or expression tree type with parameter types `T₁...Tᵥ` and return type `Tₓ`, and overload resolution of `E` with the types `T₁...Tᵥ` yields a single method with return type `U`, then a *lower-bound inference* is made *from* `U` *to* `Tₓ`. | ||
| - If `E` is an address-of method group and `T` is a function pointer type (§function-pointers) then with parameter types `T₁..Tₖ` and return type `Tₓ`, and overload resolution of `E` with the types `T₁..Tₖ` yields a single method with return type `U`, then a *lower-bound inference* is made from `U` to `Tₓ`. | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - Otherwise, if `E` is an expression with type `U`, then a *lower-bound inference* is made *from* `U` *to* `T`. | ||
| - Otherwise, no inferences are made. | ||
|
|
||
|
|
@@ -843,15 +853,25 @@ | |
| - `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*<V₂..Vₖ, V₁>` and there is a function pointer type `delegate*<U₂..Uₖ, U₁>` such that `U` is identical to `delegate*<U₂..Uₖ, U₁>`, and the calling convention of `V` is identical to `U`, and the refness of `Vᵢ` is identical to `Uᵢ`. | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - (The “uniqueness” restriction means that in the case interface `C<T>{} class U: C<X>, C<Y>{}`, then no inference is made when inferring from `U` to `C<T>` because `U₁` could be `X` or `Y`.) | ||
|
|
||
| If any of these cases apply then an inference is made from each `Uᵢ` to the corresponding `Vᵢ` as follows: | ||
| - If `Uᵢ` is not known to be a reference type then an *exact inference* is made | ||
| - If `Uᵢ` is not known to be a reference type then an *exact inference* is made; or alternatively, if `U` is not a function pointer type and `Uᵢ` is not known to be a reference type, or if `U` is a function pointer type and `Uᵢ` is not known to be a function pointer type or a reference type, then an exact inference is made | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - Otherwise, if `U` is an array type then a *lower-bound inference* is made | ||
| - Otherwise, if `V` is `C<V₁...Vₑ>` then inference depends on the `i-th` type parameter of `C`: | ||
| - If it is covariant then a *lower-bound inference* is made. | ||
| - 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*<V₂..Vₖ, V₁>` then inference depends on the i-th parameter of `delegate*<V₂..Vₖ, V₁>`: | ||
| - If V1: | ||
| - If the return is by value, then a lower-bound inference is made. | ||
| - If the return is by reference, then an exact inference is made. | ||
| - If V2..Vk: | ||
| - If the parameter is by value, then an upper-bound inference is made. | ||
| - If the parameter is by reference, then an exact inference is made. | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - Otherwise, no inferences are made. | ||
|
|
||
| #### 12.6.3.12 Upper-bound inferences | ||
|
|
@@ -864,15 +884,25 @@ | |
| - `U` is one of `IEnumerable<Uₑ>`, `ICollection<Uₑ>`, `IReadOnlyList<Uₑ>`, `IReadOnlyCollection<Uₑ>` or `IList<Uₑ>` and `V` is a single-dimensional array type `Vₑ[]` | ||
| - `U` is the type `U1?` and `V` is the type `V1?` | ||
| - `U` is constructed class, struct, interface or delegate type `C<U₁...Uₑ>` and `V` is a `class, struct, interface` or `delegate` type which is `identical` to, `inherits` from (directly or indirectly), or implements (directly or indirectly) a unique type `C<V₁...Vₑ>` | ||
| - `U` is a function pointer type (§function-pointers) then `delegate*<U₂..Uₖ, U₁>` and `V` is a function pointer type which is identical to `delegate*<V₂..Vₖ, V₁>`, and the calling convention of `U` is identical to `V`, and the refness of `Uᵢ` is identical to `Vᵢ`. | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - (The “uniqueness” restriction means that given an interface `C<T>{} class V<Z>: C<X<Z>>, C<Y<Z>>{}`, then no inference is made when inferring from `C<U₁>` to `V<Q>`. Inferences are not made from `U₁` to either `X<Q>` or `Y<Q>`.) | ||
|
|
||
| If any of these cases apply then an inference is made from each `Uᵢ` to the corresponding `Vᵢ` as follows: | ||
| - If `Uᵢ` is not known to be a reference type then an *exact inference* is made | ||
| - If `U` is not a function pointer type and `Uᵢ` is not known to be a reference type, or if `U` is a function pointer type and `Uᵢ` is not known to be a function pointer type or a reference type, then an *exact inference* is made | ||
| > *Note*: Function-pointer type-related text is only applicable in unsafe code. *end note* | ||
| - Otherwise, if `V` is an array type then an *upper-bound inference* is made | ||
| - Otherwise, if `U` is `C<U₁...Uₑ>` then inference depends on the `i-th` type parameter of `C`: | ||
| - If it is covariant then an *upper-bound inference* is made. | ||
| - 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*<U₂..Uₖ, U₁>` then inference depends on the i-th parameter of `delegate*<U₂..Uₖ, U₁>`: | ||
| - If `U1`: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as earlier, I think we can do better than this. |
||
| - If the return is by value, then an upper-bound inference is made. | ||
| - If the return is by reference, then an exact inference is made. | ||
| - If `U2`..`Uk`: | ||
| - If the parameter is by value, then a lower-bound inference is made. | ||
| - If the parameter is by reference, then an exact inference is made. | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - Otherwise, no inferences are made. | ||
|
|
||
| #### 12.6.3.13 Fixing | ||
|
|
@@ -1098,6 +1128,10 @@ | |
| - 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*`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
|
|
||
| > *Note*: This is only applicable in unsafe code, and permits overloading on `void*` and a `delegate*` allowing the `&` operator to distinguish between the two. *end note* | ||
|
|
||
| #### 12.6.4.4 Better parameter-passing mode | ||
|
|
||
| It is permitted to have corresponding parameters in two overloaded methods differ only by parameter-passing mode provided one of the two parameters has value-passing mode, as follows: | ||
|
|
@@ -1118,6 +1152,8 @@ | |
|
|
||
| - `E` exactly matches `T₁` and `E` does not exactly match `T₂` ([§12.6.4.6](expressions.md#12646-exactly-matching-expression)) | ||
| - `E` exactly matches both or neither of `T₁` and `T₂`, and `T₁` is a better conversion target than `T₂` ([§12.6.4.7](expressions.md#12647-better-conversion-target)) | ||
| - `V` is a function pointer type `delegate*<V₂..Vₖ, V₁>` and `U` is a function pointer type `delegate*<U₂..Uₖ, U₁>`, and the calling convention of `V` is identical to `U`, and the refness of `Vᵢ` is identical to `Uᵢ`. | ||
| > *Note*: This is only applicable in unsafe code. *end note* | ||
| - `E` is a method group ([§12.2](expressions.md#122-expression-classifications)), `T₁` is compatible ([§21.4](delegates.md#214-delegate-compatibility)) with the single best method from the method group for conversion `C₁`, and `T₂` is not compatible with the single best method from the method group for conversion `C₂` | ||
|
|
||
| #### 12.6.4.6 Exactly matching expression | ||
|
|
@@ -1977,6 +2013,25 @@ | |
| - Otherwise, if the *invocation_expression* invokes a returns-by-ref method ([§15.6.1](classes.md#1561-general)) or a returns-by-ref delegate, the result is a variable with an associated type of the return type of the method or delegate. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. | ||
| - Otherwise, the *invocation_expression* invokes a returns-by-value method ([§15.6.1](classes.md#1561-general)) or returns-by-value delegate, and the result is a value, with an associated type of the return type of the method or delegate. If the invocation is of an instance method, and the receiver is of a class type `T`, the associated type is picked from the first declaration or override of the method found when starting with `T` and searching through its base classes. | ||
|
|
||
| > *Note*: The following is only applicable in unsafe code. *end note* | ||
|
|
||
| The *method_declaration* [§15.6.1](classes.md#1561-general) for an unmanaged method shall have the attribute `System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute`. The use of this attribute on a method results in the following constraints: | ||
|
|
||
| - It is an error to directly call that method from C#. Instead, one can obtain a function pointer (§function-pointers) to that method and then invoke the method via that pointer. | ||
| - It is an error for that method to have a parameter or return type that is not an `unmanaged_type` ([§8.8](types.md#88-unmanaged-types)). | ||
| - It is an error for that method to have type parameters, even if those type parameters are constrained to `unmanaged`. | ||
| - 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*. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's try to avoid the stuttering in this part:
|
||
|
|
||
| The run-time processing of a function pointer invocation of the form `F(A)`, where `F` is a *primary_expression* of a *funcptr_type* and `A` is an optional *argument_list*, consists of the following steps: | ||
|
|
||
| - `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. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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. |
||
| - Otherwise, `F` points to a method. Function member invocation ([§12.6.6](expressions.md#1266-function-member-invocation)) is performed on the method to which `F` points. | ||
|
|
||
| #### 12.8.10.2 Method invocations | ||
|
|
||
| For a method invocation, the *primary_expression* of the *invocation_expression* shall be a method group. The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the *argument_list*. | ||
|
|
||
There was a problem hiding this comment.
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"?