# @main in a single Swift file?

**URL:** <https://forums.swift.org/t/main-in-a-single-swift-file/63079>\
**Category:** Using Swift\
**Created:** [February 11, 2023, 8:50pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079 "2023-02-11T20:50:58Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![joshw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joshw/32/34626_2.png) [@joshw](https://forums.swift.org/u/joshw)\
**Post date:** [February 11, 2023, 8:50pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/1 "2023-02-11T20:50:58Z")

</div>

I've got a simple Swift script that I'm trying to run:

```swift
// swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51) 
// Target: arm64-apple-macosx13.0
// Testing.swift
@main
struct Testing {
    static func main() {
        print("Hello, world!")
    }
}

```

However when I run `swift Testing.swift` from the command line, I get the following error:

```swift
Testing.swift:1:1: error: 'main' attribute cannot be used in a module that contains top-level code
@main
^
Testing.swift:1:1: note: top-level code defined in this source file
@main
^

```

Other posts seem to suggest that this happens when a file is named `main.swift`, but the file is named `Testing.swift` and there are no other files in the directory.

Am I missing something obvious here? Does `@main` work in single file scripts?

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [February 11, 2023, 9:03pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/2 "2023-02-11T21:03:18Z")

</div>

compile with `-parse-as-library`.

---

<div class="post-metadata">

**Author:** ![joshw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joshw/32/34626_2.png) [@joshw](https://forums.swift.org/u/joshw)\
**Post date:** [February 13, 2023, 6:43pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/3 "2023-02-13T18:43:41Z")

</div>

Thanks - so the whole flow is:

```swift
swiftc -parse-as-library Testing.swift
./Testing

```

Surprised that @main isn't something that works out of the box with `swift` - seems like a bug perhaps? Or is there a reason it's not supported?

For context - I'm testing out the latest from vscode swift and regardless of how I run it in the terminal the editor will show an error on the line with `@main`. Might be confusing for first timers.

 ![Screenshot 2023-02-13 at 10.44.21 AM](https://global.discourse-cdn.com/swift/original/3X/0/4/0481360c9dead53c89f90312c79e5dcce0858997.png)

---

<div class="post-metadata">

**Author:** ![Gavin\_Eadie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gavin_eadie/32/40830_2.png) [@Gavin\_Eadie](https://forums.swift.org/u/Gavin_Eadie)\
**Post date:** [February 14, 2023, 3:52pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/4 "2023-02-14T15:52:00Z")

</div>

I've the same problem, but only on Windows, and not in a script but a command-line app. Your recent screenshot is from Visual Studio and your first entry contains `Target: arm64-apple-macosx13.0`. Do both platforms suffer?

---

<div class="post-metadata">

**Author:** ![joshw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joshw/32/34626_2.png) [@joshw](https://forums.swift.org/u/joshw)\
**Post date:** [February 14, 2023, 6:34pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/5 "2023-02-14T18:34:02Z")

</div>

Yes I'm on macOS. Sounds like @main doesn't work unless the special compiler flag is used or it's inside a package.

Removing the `@main` and instead adding `Testing.main()` at the top level works fine with `swift Testing.swift`:

```swift
struct Testing {
    static func main() {
        print("Hello, world!")
    }
}

Testing.main()

```

It's an easy workaround but I was just surprised that @main doesn't work out of the box.

---

<div class="post-metadata">

**Author:** ![nnnnnnnn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nnnnnnnn/32/264_2.png) [@nnnnnnnn](https://forums.swift.org/u/nnnnnnnn)\
**Post date:** [February 14, 2023, 6:48pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/6 "2023-02-14T18:48:31Z")

</div>

It would be great if instead of emitting this error, the compiler just set the `-parse-as-library` flag and started over from the top.

---

<div class="post-metadata">

**Author:** ![allevato](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/allevato/32/385_2.png) [@allevato](https://forums.swift.org/u/allevato)\
**Post date:** [February 14, 2023, 6:51pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/7 "2023-02-14T18:51:21Z")

</div>

The rules around how top-level code is treated are somewhat nuanced:

- `@main` is forbidden in files that allow top-level statements, because the top-level statements provide an implicit entry point.

- If you invoke the compiler without any special flags and pass it a single source file, it is treated as top-level code (meaning `@main` is forbidden; you just write the top-level statements instead). Passing `-parse-as-library` to the compiler disables this top-level behavior.

- If you invoke the compiler without any special flags and pass it multiple source files, all files are treated as library code (top-level statements are forbidden), unless one of the files you pass is named `main.swift`, in which case the top-level statements in _that_ specific file provide the `main` function.

IMO, there's not much of an advantage to writing `@main` explicitly in small single-file scripts; the real power of that attribute is when it's used in a library where some protocol defines the entry point and you just conform to that protocol and get it for free. But clearly folks do want to use it directly even for small scripts, so (in a slight variation to Nate's comment above) I wonder if we'd break compatibility with anything if we just had the frontend use a "deferred main decision" by default and decide whether to allow top-level statements based on whether it detected `@main` in the file.

---

<div class="post-metadata">

**Author:** ![Jon\_Shier](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_shier/32/921_2.png) [@Jon\_Shier](https://forums.swift.org/u/Jon_Shier)\
**Post date:** [February 14, 2023, 6:59pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/8 "2023-02-14T18:59:59Z")

</div>

> [@allevato](#):
>
> IMO, there's not much of an advantage to writing `@main` explicitly in small single-file scripts; the real power of that attribute is when it's used in a library where some protocol defines the entry point and you just conform to that protocol and get it for free.

Have async top-level statements shipped yet? That's the only reason I use `@main`, it's the only way to get an async entry point into a simple executable.

---

<div class="post-metadata">

**Author:** ![allevato](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/allevato/32/385_2.png) [@allevato](https://forums.swift.org/u/allevato)\
**Post date:** [February 14, 2023, 7:06pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/10 "2023-02-14T19:06:33Z")

</div>

> [@Jon\_Shier](#):
>
> Have async top-level statements shipped yet? That's the only reason I use `@main`, it's the only way to get an async entry point into a simple executable.

I believe that was implemented in [this PR](https://github.com/apple/swift/pull/41061), which shipped in Swift 5.7. But I don't have a lot of experience with the feature directly.

---

<div class="post-metadata">

**Author:** ![joshw](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joshw/32/34626_2.png) [@joshw](https://forums.swift.org/u/joshw)\
**Post date:** [February 14, 2023, 7:06pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/11 "2023-02-14T19:06:35Z")

</div>

Gotcha - thanks for clarifying, makes sense!

For what it's worth I was working off of [this stack overflow question](https://stackoverflow.com/questions/24105690/what-is-the-entry-point-of-swift-code-execution).

Since @main is described in it's proposal `A Swift language feature for designating a type as the entry point for beginning program execution.` IMO it could be confusing for first timers who are doing some simple googling to test out some Swift, see @main is the latest in how to designate an entry point, then get hit with a somewhat cryptic error when they do.

So yeah, detecting @main and deciding whether to allow top-level statements sounds like a great solution!

---

<div class="post-metadata">

**Author:** ![etcwilde](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/etcwilde/32/13687_2.png) [@etcwilde](https://forums.swift.org/u/etcwilde)\
**Post date:** [February 14, 2023, 7:52pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/12 "2023-02-14T19:52:37Z")

</div>

@Jon_Shier

> Have async top-level statements shipped yet? That's the only reason I use `@main` , it's the only way to get an async entry point into a simple executable.

Yes, yes it has.

Now back to the original question, @joshw

Newer compilers have a note suggesting you use `-parse-as-library` if the `@main` is intentional. [Adding actionable note to @main error by etcwilde · Pull Request #59919 · apple/swift · GitHub](https://github.com/apple/swift/pull/59919)

It's still not a _great_ situation though, and I'm regularly getting pulled into conversations and questions on it. So, story time!

So. first, the name of the flag is confusing. You have an executable, not a library. It comes from an implementation detail in the C++ parser. There's an enum that tells the parser how it's parsing the file.

> <https://github.com/apple/swift/blob/b51cbb637b572f1de6d038a1d4a36b8d338c41bf/include/swift/AST/Module.h#L101-L107>

A "Library", as far as the parser is concerned, is any normal Swift code. a "Main" swift file is top-level code. `-parse-as-library` just forces the parser to set the main source file's `SourceFileKind` to `Library` instead of `Main`.

The current heuristic used looks at the number of files and their names. Any single file passed to the parser is given the "Main" `SourceFileKind`. If more than one file is passed to the compiler, any file with the name "main.swift" will be given the `SourceFileKind` of "main". It's important to note here that SwiftPM and Xcode both have different heuristics than the compiler, I'm only referring to the compiler.

Okay, so why are we in this mess in the first place? Parsing top-level code and a normal Swift file is different, and the resulting parse trees are not the same. Unfortunately, in order to know whether we have an `@main` attribute, we have to parse. By the time we know we have an `@main`, it's too late because we've already parsed incorrectly. I've considered the trade-off of tearing everything down and re-starting the parse in `Library` mode. The new Swift-Parser work does things differently. I'm hoping that I'll be able to fish the attribute out of the token stream before any ASTs are formed, and make that judgement early.  
With the current parser, there's not really a point between the lexing and parsing where I can check before it's too late. The alternative to manually using `-parse-as-library` would be to tear everything down and restart the compilation when we get it wrong. It's a tradeoff. Maybe the wrong one? To balance things out a bit, I added the note to newer compilers to recommend adding `-parse-as-library` if the `@main` is intentional. That way you have some options and aren't just left in the dark.

Hopefully that helps shed some light on what is going on here.

---

<div class="post-metadata">

**Author:** ![nnnnnnnn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nnnnnnnn/32/264_2.png) [@nnnnnnnn](https://forums.swift.org/u/nnnnnnnn)\
**Post date:** [February 14, 2023, 8:56pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/13 "2023-02-14T20:56:37Z")

</div>

IMO the preferred trade-off would be to emit a warning (instead of an error) with the `-parse-as-library` recommendation, and then tear things down and reparse as a library anyway. If `@main` exists, the intent is clear, so the error is just an unpleasant user experience with the tools.

---

<div class="post-metadata">

**Author:** ![Gavin\_Eadie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gavin_eadie/32/40830_2.png) [@Gavin\_Eadie](https://forums.swift.org/u/Gavin_Eadie)\
**Post date:** [February 14, 2023, 8:58pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/14 "2023-02-14T20:58:59Z")

</div>

> [@taylorswift](#):
>
> compile with `-parse-as-library`

.. for anyone following in my footsteps, using SPM, the magical incantation is:

```
targets: [
    .executableTarget(
        name: "PropApp",
        dependencies: ["PropLib"],
        swiftSettings: [
            .unsafeFlags(["-parse-as-library"])
        ]),
]

```

---

<div class="post-metadata">

**Author:** ![malhal](https://avatars.discourse-cdn.com/v4/letter/m/838e76/32.png) [@malhal](https://forums.swift.org/u/malhal)\
**Post date:** [April 25, 2023, 9:29am UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/15 "2023-04-25T09:29:34Z")

</div>

It's simpler to leave in the `@main` and just rename `main.swift` to `Testing.swift`

---

<div class="post-metadata">

**Author:** ![giik](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/giik/32/27110_2.png) [@giik](https://forums.swift.org/u/giik)\
**Post date:** [November 3, 2024, 4:01pm UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/16 "2024-11-03T16:01:35Z")

</div>

> [@joshw](#):
>
> Since @main is described in it's proposal `A Swift language feature for designating a type as the entry point for beginning program execution.` IMO it could be confusing for first timers who are doing some simple googling to test out some Swift, see @main is the latest in how to designate an entry point, then get hit with a somewhat cryptic error when they do.

Apologies for waking up an old thread but I just got bitten by this. I am surprised the issue has not been addressed.

Take any programming language and you will be able to find some pretty straightforward—and crucially, **working** —instructions on how to write the simplest of programs, _e.g.,_ one that outputs "Hello World!" Following these instructions will reliably produce a working program. This is sill not the case for Swift where there are instructions and following them reliably does **not** work. Imagine the disappointment of any enthusiasts, kids, etc., who want to try out the simplest "hello world" program and as a result end up confronting cryptic error messages incl. advice that this behavior is a known compiler bug (is it? for how long?) and so on.

From a software engineering perspective this does not sound like a difficult problem: (1) the compiler behavior is defined formally (I think [this description of the main attribute](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/attributes/#main)  
and [this proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0281-main-attribute.md#proposed-solution) qualify), and (2), the compiler is implemented to comply with the prescribed behavior. But [the issue](https://github.com/swiftlang/swift/issues/55127) appears to persist.

I understand that there are "workarounds" but they are essentially hacks and can be very discouraging to enthusiasts during their first steps learning and using Swift.

Do we know if someone is working on fixing this?

---

<div class="post-metadata">

**Author:** ![sspringer](https://avatars.discourse-cdn.com/v4/letter/s/e9c0ed/32.png) [@sspringer](https://forums.swift.org/u/sspringer)\
**Post date:** [November 4, 2024, 4:38am UTC](https://forums.swift.org/t/main-in-a-single-swift-file/63079/17 "2024-11-04T04:38:02Z")

</div>

> [@giik](#):
>
> Following these instructions will reliably produce a working program.

There is the “Getting started” link on the top of the [swift.org](https://www.swift.org) page. There, you have the [Start tutorial](https://www.swift.org/getting-started/cli-swiftpm/) link for Command-line tools.

> [@giik](#):
>
> Apologies for waking up an old thread but I just got bitten by this.

It might indeed be better to open a new topic if e.g. the documentation is not clear (maybe with a reference to the old topic), because generally not all contributors of the old ticket are still interested in getting updates on it.
