diff --git a/standard/attributes.md b/standard/attributes.md index 6afb317d8..4d623891f 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -539,7 +539,7 @@ A class that is decorated with the `AttributeUsage` attribute shall derive from #### 22.5.3.1 General -The attribute `Conditional` enables the definition of ***conditional methods*** and ***conditional attribute classes***. +The attribute `Conditional` enables the definition of ***conditional methods***, ***conditional local functions***, and ***conditional attribute classes***. #### 22.5.3.2 Conditional methods @@ -688,6 +688,12 @@ The use of conditional methods in an inheritance chain can be confusing. Calls m > > *end example* +#### §conditional-local-function Conditional local functions + +A local function may be made conditional in the same sense as a conditional method ([§22.5.3.2](attributes.md#22532-conditional-methods)). + +A conditional local function shall have the modifier `static`. + #### 22.5.3.3 Conditional attribute classes An attribute class ([§22.2](attributes.md#222-attribute-classes)) decorated with one or more `Conditional` attributes is a conditional attribute class. A conditional attribute class is thus associated with the conditional compilation symbols declared in its `Conditional` attributes. @@ -852,6 +858,39 @@ For invocations that occur within field or event initializers, the member name u For invocations that occur within declarations of instance constructors, static constructors, finalizers and operators the member name used is implementation-dependent. +> *Example*: Consider the following: +> +> +> ```csharp +> class Program +> { +> static void Main() +> { +> F1(); +> +> void F1([CallerMemberName] string? name = null) +> { +> Console.WriteLine($"F1 MemberName: |{name}|"); +> F2(); +> } +> +> static void F2([CallerMemberName] string? name = null) +> { +> Console.WriteLine($"F2 MemberName: |{name}|"); +> } +> } +> } +> ``` +> +> which produces the output +> +> ```console +> F1 MemberName: |Main| +> F2 MemberName: |Main| +> ``` +> +> This attribute supplies the name of the calling function member, which for local function `F1` is the method `Main`. And even though `F2` is called by `F1`, a local function is *not* a function member, so the reported caller of `F2` is also `Main`. *end example* + ## 22.6 Attributes for interoperation For interoperation with other languages, an indexer may be implemented using indexed properties. If no `IndexerName` attribute is present for an indexer, then the name `Item` is used by default. The `IndexerName` attribute enables a developer to override this default and specify a different name. diff --git a/standard/statements.md b/standard/statements.md index a8fc58a60..72ad0fb0f 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -487,15 +487,15 @@ A *local_function_declaration* declares a local function. ```ANTLR local_function_declaration - : local_function_modifier* return_type local_function_header + : attributes? local_function_modifier* return_type local_function_header local_function_body - | ref_local_function_modifier* ref_kind ref_return_type + | attributes? ref_local_function_modifier* ref_kind ref_return_type local_function_header ref_local_function_body ; local_function_header - : identifier '(' formal_parameter_list? ')' - | identifier type_parameter_list '(' formal_parameter_list? ')' + : identifier parameter_list? + | identifier type_parameter_list parameter_list? type_parameter_constraints_clause* ; @@ -505,18 +505,21 @@ local_function_modifier ; ref_local_function_modifier - : unsafe_modifier // unsafe code support + : 'extern' + | unsafe_modifier // unsafe code support ; local_function_body : block | '=>' null_conditional_invocation_expression ';' | '=>' expression ';' + | ';' ; ref_local_function_body : block | '=>' 'ref' variable_reference ';' + | ';' ; ``` @@ -561,7 +564,11 @@ Unless specified otherwise below, the semantics of all grammar elements is the s The *identifier* of a *local_function_declaration* must be unique in its declared block scope, including any enclosing local variable declaration spaces. One consequence of this is that overloaded *local_function_declaration*s are not allowed. -A *local_function_declaration* may include one `async` ([§15.15](classes.md#1515-async-functions)) modifier and one `unsafe` ([§23.1](unsafe-code.md#231-general)) modifier. If the declaration includes the `async` modifier then the return type shall be `void` or a `«TaskType»` type ([§15.15.1](classes.md#15151-general)). The `unsafe` modifier uses the containing lexical scope. The `async` modifier does not use the containing lexical scope. It is a compile-time error for *type_parameter_list* or *formal_parameter_list* to contain *attributes*. +A *local_function_declaration* may include a set of *attributes* ([§22](attributes.md#22-attributes)), one `async` ([§15.15](classes.md#1515-async-functions)) modifier, one `extern` ([§15.6.8](classes.md#1568-external-methods)) modifier, and one `unsafe` ([§23.1](unsafe-code.md#231-general)) modifier. If the declaration includes the `async` modifier then the return type shall be `void` or a `«TaskType»` type ([§15.15.1](classes.md#15151-general)). The `unsafe` modifier uses the containing lexical scope. The `async` modifier does not use the containing lexical scope. + +An external local function shall have the modifier `static`, and its *local_function_body* or *ref_local_function_body* shall be a semicolon. + +A *local_function_body* or *ref_local_function_body* shall be a semicolon only for an external local function. A local function is declared at block scope, and that function may capture variables from the enclosing scopes. It is a compile-time error if a captured variable is read by the body of the local function but is not definitely assigned before each call to the function. The compiler shall determine which variables are definitely assigned on return ([§9.4.4.33](variables.md#94433-rules-for-variables-in-local-functions)).