Skip to content

Add support for top level statements #980

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

Draft
wants to merge 10 commits into
base: draft-v9
Choose a base branch
from
2 changes: 1 addition & 1 deletion standard/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,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.

Expand Down
177 changes: 177 additions & 0 deletions standard/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

## 7.1 Application startup

### §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-defined 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:

- It shall have the name `Main`.
Expand Down Expand Up @@ -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. 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.

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.

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` 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<int> «Main»(string[] args)`

This is illustrated by the following sets of top-level statements:

**Set 1**

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements2A", expectedOutput:["cmd-line args length = 0"]} -->
```csharp
using System;
Console.WriteLine($"cmd-line args length = {args.Length}");
C c = new C();
public class C {}
```

whose implementation-generated code is

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements2B", expectedOutput:["cmd-line args length = 0"]} -->
```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**

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements3A", expectedOutput:["Hi!"]} -->
```csharp
System.Console.WriteLine("Hi!");
return 2;
```

whose implementation-generated code is

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements3B", expectedOutput:["Hi!"]} -->
```csharp
partial class Program
{
static int «Main»(string[] args)
{
System.Console.WriteLine("Hi!");
return 2;
}
}
```

**Set 3**

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements4A", expectedOutput:["Hi!"]} -->
```csharp
using System;
using System.Threading.Tasks;
await Task.Delay(1000);
Console.WriteLine("Hi!");
```

whose implementation-generated code is

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements4B", expectedOutput:["Hi!"]} -->
```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**

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements5A", expectedOutput:["Hi!"]} -->
```csharp
using System;
using System.Threading.Tasks;
await Task.Delay(1000);
Console.WriteLine("Hi!");
return 0;
```

whose implementation-generated code is

<!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements5B", expectedOutput:["Hi!"]} -->
```csharp
using System;
using System.Threading.Tasks;
partial class Program
{
static async Task<int> «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:
>
> <!-- Example: {template:"standalone-console-without-using", name:"TopLevelStatements6"} -->
> ```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.
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions standard/namespaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*
;
```

Expand Down
1 change: 1 addition & 0 deletions standard/portability-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)).
Expand Down