From 96bbe1d3a33f4e11a371d339f74817e427f46ee0 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 1 Nov 2023 09:04:42 -0400 Subject: [PATCH 01/10] Add support for top-level statements --- standard/basic-concepts.md | 179 ++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 1773345ea..5d8042ee6 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -2,7 +2,16 @@ ## 7.1 Application startup -A program may be compiled either as a ***class library*** to be used as part of other applications, or as an ***application*** that may be started directly. The mechanism for determining this mode of compilation is implementation-defined and external to this specification. +### §app-startup-general General + +A program may be compiled either as a ***class library*** to be used as part of other applications, or as an ***application*** that may be started directly. The mechanism for determining this mode of compilation is implementation-specific and external to this specification. + +The entry point of an application may be specified in either of the following ways: + +1. Explicitly, by declaring a method with appropriate characteristics (§named-entry-point). +1. Implicitly, by using top-level statements (§top-level-statements). + +### §named-entry-point Using a named entry point A program compiled as an application shall contain at least one method qualifying as an entry point by satisfying the following requirements: @@ -47,6 +56,166 @@ If the effective entry point’s return type is `int`, the return value from the Other than the situations listed above, entry point methods behave like those that are not entry points in every respect. In particular, if the entry point is invoked at any other point during the application’s lifetime, such as by regular method invocation, there is no special handling of the method: if there is a parameter, it may have an initial value of `null`, or a non-`null` value referring to an array that contains null references. Likewise, the return value of the entry point has no special significance other than in the invocation from the execution environment. +### §top-level-statements Using top-level statements + +Any one compilation unit ([§14.2](namespaces.md#142-compilation-units) in an application may contain one or more *statement_list*s—collectively called ***top-level statements***— in which case, the meaning is as if those *statement_list*s were combined in the block body of a static method within a partial class called `Program` in the global namespace, as follows: + +```csharp +partial class Program +{ + static «AsyncAndReturnType» «Main»(string[] args) + { + // top-level statements + } +} +``` + +The class name `Program` shall be referenceable by name from within the application. However, the method name `«Main»` is used here for illustrative purposes only. The actual name generated by the implementation is unspecified, and cannot be referenced by name from within the application. + +The method is designated as the entry point of the program. Explicitly declared methods (including static ones called `Main`) that by convention could be considered as entry point candidates (§named-entry-point) shall be ignored for that purpose. + +The entry-point method has one formal parameter, `string[] args`. This parameter is in scope within the top-level statements and not otherwise. Regular name conflict/shadowing rules apply. + +Async operations are allowed in top-level statements to the degree they are allowed in statements within a named async entry-point method. However, they are not required. + +The tokens that are generated in place of `«AsyncAndReturnType»` are determined based on operations used by the top-level statements, as follows: + +| **Top-level code contains** | **Generated entry-point signature** +| ----------------------- | ------------------------------- +| No `await` or `return` | `static void «Main»(string[] args)` +| `return` only | `static int «Main»(string[] args)` +| `await` only | `static async Task «Main»(string[] args)` +| `await` and `return` | `static async Task «Main»(string[] args)` + +This is illustrated by the following sets of top-level statements: + +**Set 1** + + +```csharp +using System; +Console.WriteLine($"cmd-line args length = {args.Length}"); +C c = new C(); +public class C {} +``` + +whose implementation-generated code is + + +```csharp +using System; +partial class Program +{ + static void «Main»(string[] args) + { + Console.WriteLine($"cmd-line args length = {args.Length}"); + C c = new C(); + } +} +public class C {} +``` + +*Note*: As required by the grammar for *compilation_unit* ([§14.2](namespaces.md#142-compilation-units)), top-level statements must come after *using_directive*s and before *namespace_member_declaration*s, such as types. *end note* + +**Set 2** + + +```csharp +System.Console.WriteLine("Hi!"); +return 2; +``` + +whose implementation-generated code is + + +```csharp +partial class Program +{ + static int «Main»(string[] args) + { + System.Console.WriteLine("Hi!"); + return 2; + } +} +``` + +**Set 3** + + +```csharp +using System; +using System.Threading.Tasks; +await Task.Delay(1000); +Console.WriteLine("Hi!"); +``` + +whose implementation-generated code is + + +```csharp +using System; +using System.Threading.Tasks; +partial class Program +{ + static async Task «Main»(string[] args) + { + await Task.Delay(1000); + Console.WriteLine("Hi!"); + } +} +``` + +**Set 4** + + +```csharp +using System; +using System.Threading.Tasks; +await Task.Delay(1000); +Console.WriteLine("Hi!"); +return 0; +``` + +whose implementation-generated code is + + +```csharp +using System; +using System.Threading.Tasks; +partial class Program +{ + static async Task «Main»(string[] args) + { + await Task.Delay(1000); + Console.WriteLine("Hi!"); + return 0; + } +} +``` + +The implementation-generated class `Program` can be augmented by user-written code that declares one or more partial classes called `Program`. + +> *Example*: Consider the following: +> +> +> ```csharp +> M1(); // call top-level static local function M1 +> M2(); // call top-level non-static local function M2 +> +> static void M1() { } // static local function +> void M2() { } // non-static local function +> +> Program.M1(); // call static method M1 +> new Program().M2(); // call non-static method M2 +> partial class Program +> { +> static void M1() { } // static method +> void M2() { } // non-static method +> } +> ``` +> +> As the first two declarations for `M1` and `M2` get wrapped inside the generated entry-point method, they are local functions. However, the second two declarations are methods, as they are declared inside a class rather than a method. *end example* + ## 7.2 Application termination ***Application termination*** returns control to the execution environment. @@ -708,6 +877,14 @@ Within the scope of a local variable, it is a compile-time error to refer to the > > *end note* +As described in §top-level-statements, top-level source tokens are enclosed by the generated entry-point method. + +For the purpose of simple-name evaluation, once the global namespace is reached, first, an attempt is made to evaluate the name within the generated entry point method and only if this attempt fails is the evaluation within the global namespace declaration performed. + +This could lead to name shadowing of namespaces and types declared within the global namespace as well as to shadowing of imported names. + +If the simple name evaluation occurs outside of the top-level statements and the evaluation yields a top-level local variable or function, a compile-time error results. + ### 7.7.2 Name hiding #### 7.7.2.1 General From 467a8f8e9519545bb5393e998f436c82dbe961de Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 1 Nov 2023 09:07:36 -0400 Subject: [PATCH 02/10] tweak table formatting --- standard/basic-concepts.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 5d8042ee6..3a7dba923 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -80,12 +80,12 @@ Async operations are allowed in top-level statements to the degree they are allo The tokens that are generated in place of `«AsyncAndReturnType»` are determined based on operations used by the top-level statements, as follows: -| **Top-level code contains** | **Generated entry-point signature** -| ----------------------- | ------------------------------- -| No `await` or `return` | `static void «Main»(string[] args)` -| `return` only | `static int «Main»(string[] args)` -| `await` only | `static async Task «Main»(string[] args)` -| `await` and `return` | `static async Task «Main»(string[] args)` +| **Top-level code contains** | **Generated entry-point signature** +| ----------------------- | ------------------------------- +| No `await` or `return` | `static void «Main»(string[] args)` +| `return` only | `static int «Main»(string[] args)` +| `await` only | `static async Task «Main»(string[] args)` +| `await` and `return` | `static async Task «Main»(string[] args)` This is illustrated by the following sets of top-level statements: From 8cdc4a500d30af09afe1b0758e8e62068303de21 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 1 Nov 2023 09:10:20 -0400 Subject: [PATCH 03/10] Add support for top-level statements --- standard/namespaces.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/namespaces.md b/standard/namespaces.md index 286bdf53d..ac770dee2 100644 --- a/standard/namespaces.md +++ b/standard/namespaces.md @@ -8,12 +8,12 @@ Using directives ([§14.5](namespaces.md#145-using-directives)) are provided to ## 14.2 Compilation units -A *compilation_unit* consists of zero or more *extern_alias_directive*s followed by zero or more *using_directive*s followed by zero or one *global_attributes* followed by zero or more *namespace_member_declaration*s. The *compilation_unit* defines the overall structure of the input. +A *compilation_unit* consists of zero or more *extern_alias_directive*s followed by zero or more *using_directive*s followed by zero or one *global_attributes* followed by zero or more *statement_list*s followed by zero or more *namespace_member_declaration*s. The *compilation_unit* defines the overall structure of the input. ```ANTLR compilation_unit : extern_alias_directive* using_directive* global_attributes? - namespace_member_declaration* + statement_list* namespace_member_declaration* ; ``` From 6c7967955ce716eb660c36e97f8532434aee7d30 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 1 Nov 2023 09:12:16 -0400 Subject: [PATCH 04/10] Add support for top-level statements --- standard/portability-issues.md | 1 + 1 file changed, 1 insertion(+) diff --git a/standard/portability-issues.md b/standard/portability-issues.md index 40f1f9eb8..d565b1bc7 100644 --- a/standard/portability-issues.md +++ b/standard/portability-issues.md @@ -54,6 +54,7 @@ A conforming implementation is required to document its choice of behavior in ea ## B.4 Unspecified behavior +1. The name of the entry-point method generated to contain top-level statements (§top-level-statements). 1. The time at which the finalizer (if any) for an object is run, once that object has become eligible for finalization ([§7.9](basic-concepts.md#79-automatic-memory-management)). 1. The representation of `true` ([§8.3.9](types.md#839-the-bool-type)). 1. The value of the result when converting out-of-range values from `float` or `double` values to an integral type in an `unchecked` context ([§10.3.2](conversions.md#1032-explicit-numeric-conversions)). From 6fb123dbbb5894d05bcf488a6231fd34aecb253e Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 1 Nov 2023 09:19:42 -0400 Subject: [PATCH 05/10] fix md formatting --- standard/basic-concepts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 3a7dba923..bb985a996 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -9,7 +9,7 @@ A program may be compiled either as a ***class library*** to be used as part of The entry point of an application may be specified in either of the following ways: 1. Explicitly, by declaring a method with appropriate characteristics (§named-entry-point). -1. Implicitly, by using top-level statements (§top-level-statements). +1. Implicitly, by using top-level statements (§top-level-statements). ### §named-entry-point Using a named entry point @@ -213,7 +213,7 @@ The implementation-generated class `Program` can be augmented by user-written co > void M2() { } // non-static method > } > ``` -> +> > As the first two declarations for `M1` and `M2` get wrapped inside the generated entry-point method, they are local functions. However, the second two declarations are methods, as they are declared inside a class rather than a method. *end example* ## 7.2 Application termination @@ -879,7 +879,7 @@ Within the scope of a local variable, it is a compile-time error to refer to the As described in §top-level-statements, top-level source tokens are enclosed by the generated entry-point method. -For the purpose of simple-name evaluation, once the global namespace is reached, first, an attempt is made to evaluate the name within the generated entry point method and only if this attempt fails is the evaluation within the global namespace declaration performed. +For the purpose of simple-name evaluation, once the global namespace is reached, first, an attempt is made to evaluate the name within the generated entry point method and only if this attempt fails is the evaluation within the global namespace declaration performed. This could lead to name shadowing of namespaces and types declared within the global namespace as well as to shadowing of imported names. From d96edda158122fff19c2ae23b45c41cfb5efa8ff Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Sun, 12 Nov 2023 12:29:24 -0500 Subject: [PATCH 06/10] tweak generated entry-point signature table --- standard/basic-concepts.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index bb985a996..2b1e93bf7 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -80,12 +80,12 @@ Async operations are allowed in top-level statements to the degree they are allo The tokens that are generated in place of `«AsyncAndReturnType»` are determined based on operations used by the top-level statements, as follows: -| **Top-level code contains** | **Generated entry-point signature** -| ----------------------- | ------------------------------- -| No `await` or `return` | `static void «Main»(string[] args)` -| `return` only | `static int «Main»(string[] args)` -| `await` only | `static async Task «Main»(string[] args)` -| `await` and `return` | `static async Task «Main»(string[] args)` +| **Top-level code contains** | **Generated entry-point signature** +| ----------------------- | ------------------------------- +| No `await` or `return` with value | `private static void «Main»(string[] args)` +| `return` with value only | `private static int «Main»(string[] args)` +| `await` only | `private static async Task «Main»(string[] args)` +| `await` and `return` with value | `private static async Task «Main»(string[] args)` This is illustrated by the following sets of top-level statements: From 983ab8ef2066738b3640f182633d25963e231674 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 12 Sep 2024 05:36:20 -0400 Subject: [PATCH 07/10] Update basic-concepts.md --- standard/basic-concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index 2b1e93bf7..f25e16a22 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -74,7 +74,7 @@ The class name `Program` shall be referenceable by name from within the applicat The method is designated as the entry point of the program. Explicitly declared methods (including static ones called `Main`) that by convention could be considered as entry point candidates (§named-entry-point) shall be ignored for that purpose. -The entry-point method has one formal parameter, `string[] args`. This parameter is in scope within the top-level statements and not otherwise. Regular name conflict/shadowing rules apply. +The entry-point method has one parameter, `string[] args`. This parameter is in scope within the top-level statements and not otherwise. Regular name conflict/shadowing rules apply. Async operations are allowed in top-level statements to the degree they are allowed in statements within a named async entry-point method. However, they are not required. From b26910d9ecd9f3a7e7d5958d76b1cef8ffe1d7bf Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 21 May 2025 14:11:12 -0400 Subject: [PATCH 08/10] Add mention of attribute System.Runtime.CompilerServices.CallerMemberName --- standard/basic-concepts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index f25e16a22..ba5be113e 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -58,7 +58,7 @@ Other than the situations listed above, entry point methods behave like those th ### §top-level-statements Using top-level statements -Any one compilation unit ([§14.2](namespaces.md#142-compilation-units) in an application may contain one or more *statement_list*s—collectively called ***top-level statements***— in which case, the meaning is as if those *statement_list*s were combined in the block body of a static method within a partial class called `Program` in the global namespace, as follows: +Any one compilation unit ([§14.2](namespaces.md#142-compilation-units) in an application may contain one or more *statement_list*s—collectively called ***top-level statements***—in which case, the meaning is as if those *statement_list*s were combined in the block body of a static method within a partial class called `Program` in the global namespace, as follows: ```csharp partial class Program @@ -70,7 +70,7 @@ partial class Program } ``` -The class name `Program` shall be referenceable by name from within the application. However, the method name `«Main»` is used here for illustrative purposes only. The actual name generated by the implementation is unspecified, and cannot be referenced by name from within the application. +The class name `Program` shall be referenceable by name from within the application. However, the method name `«Main»` is used here for illustrative purposes only. The actual name generated by the implementation is unspecified, and cannot be referenced by name from within the application. It is, however, available via the attribute `System.Runtime.CompilerServices.CallerMemberName` ([§22.5.6.4](attributes.md#22564-the-callermembername-attribute)). The method is designated as the entry point of the program. Explicitly declared methods (including static ones called `Main`) that by convention could be considered as entry point candidates (§named-entry-point) shall be ignored for that purpose. From 2dcda8f2cade2c034720da1a443117f0ab4f3f11 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Wed, 21 May 2025 14:21:47 -0400 Subject: [PATCH 09/10] mention invoking method in top-level statement situation --- standard/attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/attributes.md b/standard/attributes.md index fdb48a1bb..89f8d5c1d 100644 --- a/standard/attributes.md +++ b/standard/attributes.md @@ -819,7 +819,7 @@ The file path may be affected by `#line` directives ([§6.5.8](lexical-structure The attribute `System.Runtime.CompilerServices.CallerMemberNameAttribute` is allowed on optional parameters when there is a standard implicit conversion ([§10.4.2](conversions.md#1042-standard-implicit-conversions)) from `string` to the parameter’s type. -If a function invocation from a location within the body of a function member or within an attribute applied to the function member itself or its return type, parameters or type parameters in source code omits an optional parameter with the `CallerMemberNameAttribute`, then a string literal representing the name of that member is used as an argument to the invocation instead of the default parameter value. +If a function invocation from a location within the body of a function member or within an attribute applied to the function member itself or its return type, parameters or type parameters in source code omits an optional parameter with the `CallerMemberNameAttribute`, then a string literal representing the name of that member is used as an argument to the invocation instead of the default parameter value. (In the case of a function invocation from a top-level statement (§top-level-statements), the member name is that generated by the implementation.) For invocations that occur within generic methods, only the method name itself is used, without the type parameter list. From 020d6102f2d384afd1d2ba73d82c35c53115e3ad Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 6 Nov 2025 14:35:25 -0500 Subject: [PATCH 10/10] fix link --- standard/basic-concepts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/basic-concepts.md b/standard/basic-concepts.md index ba5be113e..7c3cbe695 100644 --- a/standard/basic-concepts.md +++ b/standard/basic-concepts.md @@ -70,7 +70,7 @@ partial class Program } ``` -The class name `Program` shall be referenceable by name from within the application. However, the method name `«Main»` is used here for illustrative purposes only. The actual name generated by the implementation is unspecified, and cannot be referenced by name from within the application. It is, however, available via the attribute `System.Runtime.CompilerServices.CallerMemberName` ([§22.5.6.4](attributes.md#22564-the-callermembername-attribute)). +The class name `Program` shall be referenceable by name from within the application. However, the method name `«Main»` is used here for illustrative purposes only. The actual name generated by the implementation is unspecified, and cannot be referenced by name from within the application. It is, however, available via the attribute `System.Runtime.CompilerServices.CallerMemberName` ([§23.5.6.4](attributes.md#23564-the-callermembername-attribute)). The method is designated as the entry point of the program. Explicitly declared methods (including static ones called `Main`) that by convention could be considered as entry point candidates (§named-entry-point) shall be ignored for that purpose.