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

[Proposal]: Attributes on Main for top level programs #5045

Open
1 of 4 tasks
jkotas opened this issue Aug 13, 2021 · 24 comments
Open
1 of 4 tasks

[Proposal]: Attributes on Main for top level programs #5045

jkotas opened this issue Aug 13, 2021 · 24 comments

Comments

@jkotas
Copy link
Member

jkotas commented Aug 13, 2021

Attributes on Main for top level programs

  • Proposed
  • Prototype: Not Started
  • Implementation: Not Started
  • Specification: Not Started

Summary

Allow attaching attributes to Main method in top level programs.

Motivation

The primary motivation for this feature is tenabling top level programs for WinForms (see https://github.com/dotnet/designs/blob/main/accepted/2021/winforms/streamline-application-bootstrap.md). WinForms programs require the Main method to be annotated with [STAThread].

Detailed design

Add main as the new global attribute target (assembly and module are the existing global attribute targets). Attribute specified with this target will be attached to the program Main method.

Example: [main: STAThread]

The main attribute target can be used in any compilation unit. In particular, it does need to be in the same compilation file as the top level program. This is required to enable the streamlined WinForms top level programs - the attribute will be generated by source generator.

It is an error to use the main global attribute target in libraries.

Drawbacks

Alternatives

The alternative is to pass the information that is encoded via attributes on Main method via alternative channel in top level programs (e.g. attributes on assembly or config file) and modify runtime, libraries and tools to look for this information in the alternative location in addition to the existing location.

Unresolved questions

Can this be used to attach attributes to Main method even in non-top-level programs?

Design meetings

@jkotas
Copy link
Member Author

jkotas commented Aug 13, 2021

cc @jaredpar

@333fred
Copy link
Member

333fred commented Aug 14, 2021

Another alternative would be to simply use the method target here, and make it an error if it's not in the same location as the top-level statements.

@jkotas
Copy link
Member Author

jkotas commented Aug 14, 2021

make it an error if it's not in the same location as the top-level statements.

What do you mean by "same location" in this context?

@davidwengier
Copy link
Contributor

Another alternative that I personally like is entrypoint, as I think it communicates the idea a bit better.

@333fred
Copy link
Member

333fred commented Aug 14, 2021

make it an error if it's not in the same location as the top-level statements.

What do you mean by "same location" in this context?

Same file, likely above the statements.

@RikkiGibson
Copy link
Contributor

RikkiGibson commented Aug 14, 2021

Putting it directly above the statements would syntactically attach the attributes to the first top-level statement, which seems like a good thing to me. At that point it’s not clear if it’s necessary to include a ‘method’/‘main’/’entrypoint’ target, although it may be preferred for clarity. Plus we may want to reserve the meaning of an attribute on a statement for some purpose in the future.

@CyrusNajmabadi
Copy link
Member

I would have these parse like assembly level attributes. Those don't attach to the namespace member decl, they go into their own section of the tree. In this case I would not have these attach to a statement

@jkotas
Copy link
Member Author

jkotas commented Aug 14, 2021

Same file, likely above the statements.

It is important that this attribute can be generated using source generator and thus live in a separate file. If it is not possible to generate this attribute using source generator, this feature loses a lot of its appeal for streamlined WinForms top level programs.

@RussKie

This comment was marked as outdated.

@chsienki
Copy link
Contributor

There are several open questions on this feature. For the purposes of the prototype we're going with the following designs, but will revisit them in a future LDM to determine their final answers.

Syntax: Should it be [main:, [entrypoint: or something else?

  • We'll use [main: for now.

Scope: Should you be able to use [main: attributes in other files? Should they apply to regular entrypoints or just top level programs?

  • [main: can be specified in any file
  • [main: applies to any entrypoint, simple program or not

Targets: Should we introduce a corresponding AttributeTargets.Main that allows an attribute to specify that it only applies to main methods?

  • For now, no. We won't introduce a new attribute target. Users can apply any regular Method targeted attribute (or All) to the main method.

@yaakov-h
Copy link
Member

Interesting approach on those designs.

Does this mean that, completely hypothetically, the WinForms SDK could define [main: STAThread] itself rather than requiring the developer to include that on their Main() function?

@AlekseyTs
Copy link
Contributor

[main: applies to any entrypoint, simple program or not

This is going to be very tricky to implement. We determine the entry-point very late and now we will need to figure this out just to be able to bind attributes. I am not sure if the complexity worth it.

@Neme12
Copy link

Neme12 commented Jun 7, 2022

@AlekseyTs Couldn't attributes with the main target also be bound later in the pipeline, after the entry point is determined? Or alternatively, the attribute type and constructor could be bound as usual along with all other attributes, but only determine the method that it actually applies to later, after the entry point is determined?

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jun 7, 2022

Our current model is to be able to bind attributes related to a specific symbol on demand, pretty much any time. Today, all such attributes are syntactically related to symbol's declaration. All attributes are supposed to be returned through public API (GetAttributes), we do not control at which point in time a consumer is going to call it. Of course, things would be easier, if the compiler could fully control all the timing.

@MSDN-WhiteKnight
Copy link

The main attribute target can be used in any compilation unit. In particular, it does need to be in the same compilation file as the top level program. This is required to enable the streamlined WinForms top level programs - the attribute will be generated by source generator.

Is this part really required? The STA/MTA choice affects how threading works in COM interop and troubleshooting related issues, so it's better to have STAThread attribute visible next to the code it applies to. In case of WinForms it's always there and it does not matter, but for console apps i'd like to always determine quickly whether it enables STA.

Also, would WinForms benefit much from auto-generating STAThread attribute only? I think the whole Program.cs in WinForms apps is a boilerplate that people usually don't modify manually. Could WinForms work like WPF, where user sets startup window declaratively, and entry point is synthesized automatically with correct attributes?

@Neme12
Copy link

Neme12 commented Jun 8, 2022

I don't think most WinForms developers, or developers in general, use COM interop, or even know what it is. For the majority of devs, that attribute is just some weird funky thing that you're not supposed to touch.

@Neme12
Copy link

Neme12 commented Jun 8, 2022

But I agree that for WinForms, the whole entry point is something that could be generated and that people usually don't touch and aren't interested in.

@RussKie
Copy link
Member

RussKie commented Jun 9, 2022

Visual Basic hides the entry point with most configurations done via the Application Framework. That makes most (if not all) VB developers unaware of how the application is configured, and that creates frictions. So hiding the Program.cs or ther Main method away from developers would be a bad move IMO. Also, whilst some app developers may choose not to modify the content of the default Main method, others do modify it quite extensively, after all this is the place to bootstrap the applications before any UI is shown to the user.

I don't think most WinForms developers, or developers in general, use COM interop, or even know what it is.

I think you'd be surprised.

@Sour-Codes
Copy link

Kicking up the dust about this.

The comments from the LDM 2022-03-09 meeting focused on the [STAThread] for WinForms and the unlikelihood that users need to apply it. A better argument for attribute usage on top-level statements is support for static abstract members in interfaces, since you have to apply [RequiresPreviewFeatures] to any member (or apply to the type declaration for usage throughout the type) that eventually relies on a call to a static abstract member within its block. This includes main as well. Realizing that this hits hardest when utilizing Generic Math previews (.NET 6 and .NET 7 Preview 5) as it's heavily dependent on static abstract declarations in many of its interfaces.

Found this thread and sharing this as I am writing a program now which is reporting CA2252: Using '<member>' requires opting into preview features and was confused as to how to apply [RequiresPreviewFeatures] to top-level main.

Guess I'll revert to the traditional Program.Main style statements until this is figured out.

@svick
Copy link
Contributor

svick commented Aug 18, 2022

@Sour-Codes The recommended way to enable preview features is to do it for the whole assembly by adding <EnablePreviewFeatures>true</EnablePreviewFeatures> to your csproj.

@333fred
Copy link
Member

333fred commented Aug 18, 2022

You can also do:

[RequiresPreviewFeatures] 
partial class Program { }

@Sour-Codes
Copy link

Sour-Codes commented Aug 18, 2022

@svick, this approach works if you're expecting to use preview features throughout one project. My scenario is that my library also utilizes <GenerateRequiresPreviewFeaturesAttribute>false</GenerateRequiresPreviewFeaturesAttribute> to not force dependent projects that aren't using preview features to be forced to apply the <EnablePreviewFeatures>true</EnablePreviewFeatures>, as suggested in Building a library that offers preview features.

<GenerateRequiresPreviewFeaturesAttribute>false</GenerateRequiresPreviewFeaturesAttribute> will require dependent projects to annotate with [RequiresPreviewFeatures] even if its corresponding csproj has <EnablePreviewFeatures>true</EnablePreviewFeatures>.

@Sour-Codes
Copy link

@333fred Works like a charm! 🎉🥳I missed the memo that top-level statements creates a partial class Program { } behind the scenes somewhere.

#if NET6_0_OR_GREATER

using System.Runtime.Versioning;

[RequiresPreviewFeatures]
partial class Program
{

}

#endif

@jrmoreno1
Copy link

I don't think most WinForms developers, or developers in general, use COM interop, or even know what it is.

Can’t speak for others, but when I was doing VB WInForms I was aware of and using both…and have no reason to expect that the application’s aren’t still in use, so whoever is maintaining them now probably is as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests