Skip to content
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

Add main attributes proposal #5817

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions proposals/global-attributes-for-main.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Attribute target for Simple programs

## Summary

C# should allow an attribute to target the main method / program entrypoint
without the need for it to be syntactically placed next to it.

## Motivation

Today, top level (simple) programs have no way to place attributes on the main
method, as there is no syntactic place for them to attach to. This means that
application models, such as Windows Forms, cannot take advantage of the
simplified entry point as they require additional attributes to be placed on
main (see
<https://github.com/dotnet/designs/blob/main/accepted/2021/winforms/streamline-application-bootstrap.md>).

Further if a source generator wishes to add an attribute to the main method, the
user of the generator must not use top level statements and explicitly make
their main method `partial`.

## Detailed design

A new global attribute target `main` will be recognized alongside `assembly` and
`module`, that allows an attribute to specify it should be attached to the
entrypoint of a simple / top level program.

For example:

```csharp
[main: STAThread]
```

Can be placed in any syntax tree of the compilation, and the `[STAThread]`
attribute will be attached the entrypoint of the program.

Only simple program entrypoints are supported. In the case of multiple
entrypoints the attribute will only apply to the first chosen location.
Comment on lines +36 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are not multiple entry points for programs with top level statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be. Although its an error case, the compiler explicitly allows you to have multiple files with top level statements. We're saying here that we'll only apply the attributes to the first one (same as if you call getEntryPoint and there are multiple ones).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although its an error case, the compiler explicitly allows you to have multiple files with top level statements

It's an error case. The compiler explicitly does not allow it. Therefore, our handling of such an error case is an implementation detail, and doesn't belong in the specification.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could simplify this to essentially

Only simple program entrypoints are supported. It is not supported when there is an explicit Main method which qualifies as the entry point.

I agree we should avoid listing out how it works in cases like multiple files with top level statements. That is already an error scenario, basically an invalid program. Generally we don't spec out behaviors for scenarios like that.


### Assemblies with no top level statements

It is an error to use the `[main:` target in a program that has no top level
statements. This includes assemblies with no entry point (e.g. a class library)
or a program with a regular entrypoint (e.g. `static void Main()`).

### Attribute locations

There is no new
[`AttributeTargets`](https://docs.microsoft.com/en-us/dotnet/api/system.attributetargets?view=net-6.0)
enum value defined as part of this proposal, and it is an error to prefix an
attribute with `[main:` that has a target where the `AttributeTargets.Method`
flag is not set (e.g.
`(target & AttributeTargets.Method) != AttributeTargets.Method`)

The
[`AllowMultiple`](https://docs.microsoft.com/en-us/dotnet/api/system.attributeusageattribute.allowmultiple?view=net-6.0)
field of `AttributeUsage` is respected, and it is an error to prefix an
attribute with `[main:` multiple times unless `AllowMultiple` is set to true.
This applies across all syntax trees, only one prefixed attribute is allowed
regardless of location defined.

```csharp
public class SingleAttribute : Attribute { }

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class MultiAttribute : Attribute { }

[AttributeUsage(AttributeTargets.Class)]
public class ClassOnlyAttribute : Attribute { }

/// file1.cs
[main: Single]
[main: Single] // error CS0579: Duplicate 'Single' attribute
[main: Multi]
[main: Multi]

/// file2.cs

[main: Single] // error CS0579: Duplicate 'Single' attribute
[main: Multi]
[main:ClassOnly] //error CS0592: Attribute 'ClassOnly' is not valid on this declaration type. It is only valid on 'class' declarations.
```

## Open Questions

### Targeting non simple entry points

There is some complexity in identifying exactly which method is the entrypoint
for regular (non-simple) entrypoints in the compiler. It currently happens
fairly late and we will need to bring it forward in order just to bind for this
to work.

However, if we don't do it, then it pushes that complexity onto the generator
author to identify which kind of entrypoint the user is using and change
generation strategies accordingly.

That might be considered a feature: source generators can't edit user code and
one could argue that adding attributes to a regular entrypoint without a
`partial` modifier is effectively editing it. The same argument then applies to
simple programs.

If we decide *not* to support source generation scenarios, then we could limit
the syntactic location to be required to be in the top level syntax tree itself.

An alternative solution would be to have the simple program entry point assigned
a well-know speakable identifier, and make it `partial` by default. This would
allow a generator to create the matching partial definition that includes the
attribute.

### Naming

This proposal is using `main` as straw man syntax for the real value. We should
decide exactly what to call it. Suggestions include `main` or `entrypoint`.

### Attribute Target

Should we introduce a corresponding `AttributeTargets.Main` that allows an
attribute to specify that it only applies to main methods? Currently we don't,
and users can apply any regular Method targeted attribute (or All) to the main
method.