Skip to content

Commit 69a8d66

Browse files
RexJaeschkeBillWagner
authored andcommitted
Add support for static anonymous functions
Add support for static anonymous functions Add support for static anonymous functions Add support for static anonymous functions
1 parent 1477dfc commit 69a8d66

4 files changed

Lines changed: 29 additions & 14 deletions

File tree

standard/conversions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ Anonymous functions may influence overload resolution, and participate in type i
853853
854854
### 10.7.2 Evaluation of anonymous function conversions to delegate types
855855
856-
Conversion of an anonymous function to a delegate type produces a delegate instance that references the anonymous function and the (possibly empty) set of captured outer variables that are active at the time of the evaluation. When the delegate is invoked, the body of the anonymous function is executed. The code in the body is executed using the set of captured outer variables referenced by the delegate. A *delegate_creation_expression* ([§12.8.17.5](expressions.md#128175-delegate-creation-expressions)) can be used as an alternate syntax for converting an anonymous method to a delegate type.
856+
Conversion of an anonymous function to a delegate type produces a delegate instance that references the anonymous function and, for non-`static` anonymous functions, the (possibly empty) set of captured outer variables that are active at the time of the evaluation. When the delegate is invoked, the body of the anonymous function is executed. The code in the body is executed using the set of captured outer variables referenced by the delegate. A *delegate_creation_expression* ([§12.8.17.5](expressions.md#128175-delegate-creation-expressions)) can be used as an alternate syntax for converting an anonymous method to a delegate type.
857857
858858
The invocation list of a delegate produced from an anonymous function contains a single entry. The exact target object and target method of the delegate are unspecified. In particular, it is unspecified whether the target object of the delegate is `null`, the `this` value of the enclosing function member, or some other object.
859859

standard/expressions.md

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5263,11 +5263,16 @@ An ***anonymous function*** is an expression that represents an “in-line” me
52635263
52645264
```ANTLR
52655265
lambda_expression
5266-
: 'async'? anonymous_function_signature '=>' anonymous_function_body
5266+
: anonymous_function_modifier? anonymous_function_signature '=>' anonymous_function_body
52675267
;
52685268
52695269
anonymous_method_expression
5270-
: 'async'? 'delegate' explicit_anonymous_function_signature? block
5270+
: anonymous_function_modifier? 'delegate' explicit_anonymous_function_signature? block
5271+
;
5272+
5273+
anonymous_function_modifier
5274+
: 'async' 'static'?
5275+
| 'static' 'async'?
52715276
;
52725277
52735278
anonymous_function_signature
@@ -5316,6 +5321,12 @@ anonymous_function_body
53165321
;
53175322
```
53185323

5324+
If the modifier `static` is present, the anonymous function cannot capture state from the enclosing scope.
5325+
5326+
A non-`static` local function or non-`static` anonymous function can capture state from an enclosing `static` anonymous function, but cannot capture state outside the enclosing static anonymous function.
5327+
5328+
Removing the `static` modifier from an anonymous function in a valid program does not change the meaning of the program.
5329+
53195330
When recognising an *anonymous_function_body* if both the *null_conditional_invocation_expression* and *expression* alternatives are applicable then the former shall be chosen.
53205331

53215332
<!-- markdownlint-disable MD028 -->
@@ -5365,11 +5376,11 @@ A *block* body of an anonymous function is always reachable ([§13.2](statements
53655376
> x => x + 1 // Implicitly typed, expression body
53665377
> x => { return x + 1; } // Implicitly typed, block body
53675378
> (int x) => x + 1 // Explicitly typed, expression body
5368-
> (int x) => { return x + 1; } // Explicitly typed, block body
5379+
> static (int x) => { return x + 1; } // Explicitly typed, block body
53695380
> (x, y) => x * y // Multiple parameters
53705381
> () => Console.WriteLine() // No parameters
53715382
> async (t1,t2) => await t1 + await t2 // Async
5372-
> delegate (int x) { return x + 1; } // Anonymous method expression
5383+
> static delegate (int x) { return x + 1; } // Anonymous method expression
53735384
> delegate { return 1 + 1; } // Parameter list omitted
53745385
> ```
53755386
>
@@ -5399,8 +5410,10 @@ The body (*expression* or *block*) of an anonymous function is subject to the fo
53995410
- If the anonymous function includes a signature, the parameters specified in the signature are available in the body. If the anonymous function has no signature it can be converted to a delegate type or expression type having parameters ([§10.7](conversions.md#107-anonymous-function-conversions)), but the parameters cannot be accessed in the body.
54005411
- Except for by-reference parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a by-reference parameter.
54015412
- Except for parameters specified in the signature (if any) of the nearest enclosing anonymous function, it is a compile-time error for the body to access a parameter of a `ref struct` type.
5402-
- When the type of `this` is a struct type, it is a compile-time error for the body to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule simply prohibits such access and does not affect whether member lookup results in a member of the struct.
5403-
- The body has access to the outer variables ([§12.21.6](expressions.md#12216-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§12.21.7](expressions.md#12217-evaluation-of-anonymous-function-expressions)).
5413+
- If the modifier `static` is present, it is a compile-time error for the body to access `this` or `base`.
5414+
- If the modifier `static` is absent, when the type of `this` is a struct type, it is a compile-time error for the body to access `this`. This is true whether the access is explicit (as in `this.x`) or implicit (as in `x` where `x` is an instance member of the struct). This rule simply prohibits such access and does not affect whether member lookup results in a member of the struct.
5415+
- If the modifier `static` is absent, the body has access to the outer variables ([§12.21.6](expressions.md#12216-outer-variables)) of the anonymous function. Access of an outer variable will reference the instance of the variable that is active at the time the *lambda_expression* or *anonymous_method_expression* is evaluated ([§12.21.7](expressions.md#12217-evaluation-of-anonymous-function-expressions)).
5416+
- If the modifier `static` is present, the body may use outer variable names as operands to `nameof`.
54045417
- It is a compile-time error for the body to contain a `goto` statement, a `break` statement, or a `continue` statement whose target is outside the body or within the body of a contained anonymous function.
54055418
- A `return` statement in the body returns control from an invocation of the nearest enclosing anonymous function, not from the enclosing function member.
54065419
@@ -5485,6 +5498,8 @@ An anonymous function cannot be a receiver, argument, or operand of a dynamicall
54855498
54865499
Any local variable, value parameter, or parameter array whose scope includes the *lambda_expression* or *anonymous_method_expression* is called an ***outer variable*** of the anonymous function. In an instance function member of a class, the this value is considered a value parameter and is an outer variable of any anonymous function contained within the function member.
54875500
5501+
That said, if the modifier `static` is present, the anonymous function cannot capture state from the enclosing scope. As a result, locals, parameters, and `this` from the enclosing scope are not available to that anonymous function.
5502+
54885503
#### 12.21.6.2 Captured outer variables
54895504
54905505
When an outer variable is referenced by an anonymous function, the outer variable is said to have been ***captured*** by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated ([§9.2.9.1](variables.md#9291-general)). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.
@@ -5524,7 +5539,7 @@ When an outer variable is referenced by an anonymous function, the outer variabl
55245539
>
55255540
> *end example*
55265541
5527-
When a local variable or a value parameter is captured by an anonymous function, the local variable or parameter is no longer considered to be a fixed variable ([§24.4](unsafe-code.md#244-fixed-and-moveable-variables)), but is instead considered to be a moveable variable. However, captured outer variables cannot be used in a `fixed` statement ([§24.7](unsafe-code.md#247-the-fixed-statement)), so the address of a captured outer variable cannot be taken.
5542+
When a local variable or a value parameter is captured by a non-`static` anonymous function, the local variable or parameter is no longer considered to be a fixed variable ([§24.4](unsafe-code.md#244-fixed-and-moveable-variables)), but is instead considered to be a moveable variable. However, captured outer variables cannot be used in a `fixed` statement ([§24.7](unsafe-code.md#247-the-fixed-statement)), so the address of a captured outer variable cannot be taken.
55285543
55295544
> *Note*: Unlike an uncaptured variable, a captured local variable can be simultaneously exposed to multiple threads of execution. *end note*
55305545
@@ -5563,7 +5578,7 @@ A local variable is considered to be ***instantiated*** when execution enters th
55635578
>
55645579
> *end example*
55655580
5566-
When not captured, there is no way to observe exactly how often a local variable is instantiatedbecause the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
5581+
When not captured, there is no way to observe exactly how often a local variable is instantiatedbecause the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. However, when a non-`static` anonymous function captures a local variable, the effects of instantiation become apparent.
55675582
55685583
> *Example*: The example
55695584
>
@@ -5683,7 +5698,7 @@ If a for-loop declares an iteration variable, that variable itself is considered
56835698
>
56845699
> *end example*
56855700
5686-
It is possible for anonymous function delegates to share some captured variables yet have separate instances of others.
5701+
It is possible for non-`static` anonymous function delegates to share some captured variables yet have separate instances of others.
56875702
56885703
> *Example*: For example, if `F` is changed to
56895704
>
@@ -5712,7 +5727,7 @@ It is possible for anonymous function delegates to share some captured variables
57125727
>
57135728
> *end example*
57145729
5715-
Separate anonymous functions can capture the same instance of an outer variable.
5730+
Separate non-`static` anonymous functions can capture the same instance of an outer variable.
57165731
57175732
> *Example*: In the example:
57185733
>

standard/unsafe-code.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ The `&` operator ([§24.6.5](unsafe-code.md#2465-the-address-of-operator)) permi
261261
262262
In precise terms, a fixed variable is one of the following:
263263
264-
- A variable resulting from a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) that refers to a local variable, value parameter, or parameter array, unless the variable is captured by an anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)).
264+
- A variable resulting from a *simple_name* ([§12.8.4](expressions.md#1284-simple-names)) that refers to a local variable, value parameter, or parameter array, unless the variable is captured by a non-`static` anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)).
265265
- A variable resulting from a *member_access* ([§12.8.7](expressions.md#1287-member-access)) of the form `V.I`, where `V` is a fixed variable of a *struct_type*.
266266
- A variable resulting from a *pointer_indirection_expression* ([§24.6.2](unsafe-code.md#2462-pointer-indirection)) of the form `*P`, a *pointer_member_access* ([§24.6.3](unsafe-code.md#2463-pointer-member-access)) of the form `P->I`, or a *pointer_element_access* ([§24.6.4](unsafe-code.md#2464-pointer-element-access)) of the form `P[E]`.
267267

standard/variables.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ For the purpose of definite-assignment checking, an array element is considered
7171
7272
### 9.2.5 Value parameters
7373
74-
A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist when execution of the function body completes. However, if the value parameter is captured by an anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection.
74+
A value parameter comes into existence upon invocation of the function member (method, instance constructor, accessor, or operator) or anonymous function to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter normally ceases to exist when execution of the function body completes. However, if the value parameter is captured by a non-`static` anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from that anonymous function is eligible for garbage collection.
7575
7676
For the purpose of definite-assignment checking, a value parameter is considered initially assigned.
7777
@@ -124,7 +124,7 @@ A ***local variable*** is declared by a *local_variable_declaration*, *declarati
124124
125125
A *local_variable_declaration* can occur in a *block*, a *for_statement*, a *switch_block*, or a *using_statement*. A *declaration_expression* can occur as an `out` *argument_value*, and as a *tuple_element* that is the target of a deconstructing assignment ([§12.23.2](expressions.md#12232-simple-assignment)).
126126
127-
The lifetime of a local variable is the portion of program execution during which storage is guaranteed to be reserved for it. This lifetime extends from entry into the scope with which it is associated, at least until execution of that scope ends in some way. (Entering an enclosed *block*, calling a method, or yielding a value from an iterator block suspends, but does not end, execution of the current scope.) If the local variable is captured by an anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from the anonymous function, along with any other objects that come to reference the captured variable, are eligible for garbage collection. If the parent scope is entered recursively or iteratively, a new instance of the local variable is created each time, and its initializer, if any, is evaluated each time.
127+
The lifetime of a local variable is the portion of program execution during which storage is guaranteed to be reserved for it. This lifetime extends from entry into the scope with which it is associated, at least until execution of that scope ends in some way. (Entering an enclosed *block*, calling a method, or yielding a value from an iterator block suspends, but does not end, execution of the current scope.) If the local variable is captured by a non-`static` anonymous function ([§12.21.6.2](expressions.md#122162-captured-outer-variables)), its lifetime extends at least until the delegate or expression tree created from the anonymous function, along with any other objects that come to reference the captured variable, are eligible for garbage collection. If the parent scope is entered recursively or iteratively, a new instance of the local variable is created each time, and its initializer, if any, is evaluated each time.
128128
129129
> *Note*: A local variable is instantiated each time its scope is entered. This behavior is visible to user code containing anonymous methods. *end note*
130130
<!-- markdownlint-disable MD028 -->

0 commit comments

Comments
 (0)