[Pitch] Improve Interaction Between @main and Top Level Code

Hi all,

This is a small pitch to address some sharp edges that get in the way of using @main in a single file executable, or one with a main.swift. The goal is to allow build systems to get rid of the inconsistent heuristics they currently use to build executables with -parse-as-library, and in general make parsing "just work" like users expect it to in more situations.

The full proposal is here, including a more detailed discussion of some of the source compatibility implications and tradeoffs involved.

  • Owen
23 Likes

This would be an obvious improvement. Thanks for proposing this!

2 Likes

This looks like a good improvement. It seems to address the most common cases of frustration and confusion.

I did wonder about extending the behavior to the deprecated @NSApplicationMain and @UIApplicationMain attributes.

If their use in new code should be discouraged instead of @main, I don't believe it is beneficial to make them easier to use.

This is an interesting point. In most places in the compiler, once the various main attributes are resolved, the distinction of whether it was @(NS|UI)ApplicationMain vs. @main primarily exists for diagnostic purposes. One exception is SILGen, which emits direct calls to (UI|NS)ApplicationMain for those, but I wonder if those special cases could also be removed since the (UI|NS)ApplicationDelegate protocol has a default implementation of main() now, which is what makes @main work on those types in the first place.

This change would be introducing a new, but small, dependency on those long-deprecated attributes, because it's happening in ASTGen and is based on the syntax tree, before things get boiled down to something like hasEntryPoint in the semantic layer.

So, should we use this change as the "carrot" that encourages people to stick to @main instead of using the old attributes? Or would the difference in behavior would be more confusing to users than it's worth? I could see it argued either way; I don't really have a strong feeling here.

(IMO, the next major language version should stop recognizing @(NS|UI)ApplicationMain entirely, but that would be a different proposal.)

1 Like

To me, it would seem that whatever falls out most naturally from implementing the feature for @main ought to be the behavior we get. That is, if @NSApplicationMain also gets this "for free," then so be it. And if it doesn't, so be it. (As long as it's documented.)

2 Likes

That's the point I made about the ASTGen part of the implementation linked in the proposal. There is no falling out naturally here because this change has to introduce a new check for the attribute; it's not leveraging any already existing resolution of the entry point. Here, a choice has to be made either way—it either looks for just @main or it looks for any of the three.

1 Like

Supporting all three attributes (or not) is trivial, so implementation complexity really isn't a concern here. I chose to extend the behavior to these attributes primarily because I think it's the least surprising behavior. Let's say the user writes the following when using Swift 5 mode:

// foo.swift
@UIApplicationMain
class AppDelegate: UIApplicationDelegate { ... }

Reporting error: 'UIApplicationMain' attribute cannot be used in a module that contains top-level code doesn't actually push the user to use @main instead, it's just confusing IMO. Furthermore, I don't think we could improve that diagnostic enough that continuing to reject this code would be better than just accepting it and letting deprecation warnings point out that @main is preferred.

4 Likes

As long as @NSApplicationMain and @UIApplicationMain are still accepted by the compiler, it would be confusing to treat them differently from @main here.

Yes they should go away at some point, but until that happens I would not want to have separate rules for where they can be used.

3 Likes

I'd agree that the existing diagnostic doesn't push the user to use @main and if it is not possible to improve the diagnostic to do so, then treating the deprecated attributes the same would be the way to go. Maybe mention this and the rationale in 'Alternatives Considered' section.

I definitely agree, although I guess it wouldn't simplify the compiler much since they would continue to be supported in older language modes.

It's already an error to use these attributes in the Swift 6 language mode: swift-evolution/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md at main · swiftlang/swift-evolution · GitHub, so the discussion here is already only relevant to older language modes.

1 Like

:man_facepalming: Thanks. I was only looking at the proposal that introduced @main and forgot that there was already a separate one that officially deprecated those.

In that case, I think the right answer is to handle @(NS|UI)Application here in this proposal for folks still using it in Swift 5 mode, and Swift 6 mode has already taken care of itself.

2 Likes

With SE-0383 in mind, I also agree it makes sense to handle @(NS|UI)Application as proposed.

Possibly reference SE-0383 in that section of the proposal indicating that the support for the deprecated attributes is only for Swift 5 language mode.

I've updated the proposal to clarify the NS/UIApplicationMain situation a bit based on the discussion here and moved the latest version over to Add proposal: Improve Interaction Between `@main` and Top-Level Code by owenv · Pull Request #3467 · swiftlang/swift-evolution · GitHub

2 Likes