Following tutorial gives ' 'main' attribute cannot be used in a module that contains top-level code'

I am a newbie to Swift (and a very amateur programmer). I am on Windows 11 Home and am following the tuorial at ‘ Configuring VS Code for Swift Development | Swift.org

When I try to build this starter program I get the error message
```swift

”aoc2015.swift:4:1: error: 'main' attribute cannot be used in a module that contains top-level code
1 | // The Swift Programming Language
| |- note: top-level code defined in this source file
| - note: pass '-parse-as-library' to compiler invocation if this is intentional 2 | // https://docs.swift.org/swift-book 3 | 4 | @main | - error: 'main' attribute cannot be used in a module that contains top-level code
5 | struct Aoc2015 {
6 | static func main() {
PS D:\repos\AdventOfCode\Swift\aoc_2015\aoc2015\Sources\aoc2015>”

Suggestions on how to proceed from here would be welcome.


You need to pass the -parse-as-library flag to swiftc, as the error message suggests. For simple code examples like this, you may also prefer to simply write code at the top level, instead of using @main. Then you won’t need to pass any special compiler flags.

judging by the screenshot, it's in a package, and IIRC, SPM should be passing the right flag based on whether the source file is called "main.swift" or not. I don't see any evidence in the screenshot of why this file would be treated as containing top-level code.

I'm pretty sure I've had it happen before that the LSP gets stuck & I had to quit & relaunch the IDE to get it to pass the right flag, though.

1 Like

Thanks for the responses. I played around quite abit and was able to find working solutions to the problem.

For the parse as library recommendation, this output from the compiler is very counterintuitive given that I created the project as an executable and not as a library. This suggests that the build task on the tutorial page needs updating.

I found that the ‘ swift run’ command in the terminal was all that I needed to get my project to build and run. This does give its own error message

warning: unable to create symbolic link at D:\repos\AdventOfCode\Swift\aoc_2015\aoc2015\.build\release: Error Domain=NSCocoaErrorDomain Code=512 "(null)"UserInfo={NSURL=file:///D:/repos/AdventOfCode/Swift/aoc_2015/aoc2015/.build/release, NSFilePath=D:\repos\AdventOfCode\Swift\aoc_2015\aoc2015\.build\release, NSUnderlyingError=Win32Error(code: 1
```

Copilot was able to explain that this is due to a windows error in that windows does not allow a ‘symbolic link’ to be created at the access level I was using (even though I am operating windows in developer mode).

What I take away from this is that the introduction to Swift compilation on Widows is a bit broken and consequently not a good experience for a newbie like myself.

I also encountered other problems, like creating a new file (in the correct place) with structs. These new structs were not recognised by the main program (methods on the structs could no be called and the struct name was underlined in red.) These structs did become available merely by closing and reopening vscode.

This and other experiences (I’m looking at you substrings) mean that I won’t be pursuing investigating swift.

I strongly recommend against top-level code. I started out that way on a book project I was working on (see An Introduction to Programming Using Swift - #6 by hryde) and I ran into so many problems down the road that I now swear the stuff off.

It would be nice if the compiler would automatically recognize code containing @main as an executable (not a library), but writing top-level code just to avoid having to use the -parse-as-library option is a bad habit to get into.

IMHO:
print( "Hello, World!" )
is the only program that should ever be written using top-level code :slight_smile:
Problems like lazy initialization and deinit not working are big enough problems that you should rarely, if ever, writing top-level code.

FWIW:my excuse for using top-level code for my book project is that I started it back in the days of Swift 2, long before writing top-level code was an issue. But with Swift 6 and beyond, you really don't want to do that. And a makefile (for those working from the command line) is probably a clean way of dealing with -parse-as-library.

1 Like

I generally agree. But as a practical matter, for trivial snippets of code, it allows one to build without passing an extra flag.

This is also true (but I think you mean the other way around; we want a file with a @main type to be parsed as a library, so no top-level code.)

> swiftc -help
...
-parse-as-library       Parse the input file(s) as libraries, not scripts
...

I am not a learner, but I still find this compiler flag very confusing in practice and also poorly documented.

Here is why.

I want to build my first Swift executable program.

// Foo.swift

@main
enum Foo {
   static func main () {
      print ("-->", #function, "my first Swift program")
   }
}

Building my executable.

> swiftc Foo.swift
Foo.swift:3:1: error: 'main' attribute cannot be used in a module that contains top-level code
1 | // Foo.swift
  | |- note: top-level code defined in this source file
  | `- note: pass '-parse-as-library' to compiler invocation if this is intentional
2 | 
3 | @main
  | `- error: 'main' attribute cannot be used in a module that contains top-level code
4 | enum Foo {
5 |    static func main () {

What gives?

> swiftc -parse-as-library Foo.swift
> ./Foo
--> main() my first Swift executable program

The output is an executable, not a library.

The flag has been there since the very early days, whereas the @main attribute was only added in Swift 5.3 or so. So the name is more specific than what it really does, which is prevent emission or a main function from the top level statements in the main file.

1 Like