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

Global initialization missing when linking tests against executables #5511

Open
dabrahams opened this issue May 14, 2022 · 7 comments
Open
Labels
bug swift test Changes impacting `swift test` tool

Comments

@dabrahams
Copy link
Contributor

#3316 may be somewhat incomplete IIUC. If you try to run tests on this branch, you'll find it crashes because the global constants lexer and parser are both 0x00000…

image

@grynspan
Copy link
Contributor

I was unable to reproduce this issue following the instructions above. Looks like there's no test target in the package, so no tests run and the crash does not occur. Are there additional steps I need to follow? Feel free to reopen with additional repro steps and I'll try it again.

@grynspan grynspan closed this as not planned Won't fix, can't repro, duplicate, stale Aug 13, 2024
@grynspan grynspan added the swift test Changes impacting `swift test` tool label Aug 13, 2024
@dabrahams
Copy link
Contributor Author

Sorry, just add the missing test target:

diff --git a/Package.swift b/Package.swift
index 6907080..9483b82 100644
--- a/Package.swift
+++ b/Package.swift
@@ -47,5 +47,11 @@ let package = Package(
         path: "examples/functype_ec",
         exclude: ["README.md", "Makefile"],
         plugins: [.plugin(name: "CitronParserGenerator")]),
+
+      .testTarget(
+        name: "ExampleTests",
+        // https://github.com/apple/swift-package-manager/issues/6367
+        dependencies: ["expr"]
+      ),
     ]
 )

I'll update the branch with this.

@dschaefer2
Copy link
Member

@grynspan does this help?

@grynspan
Copy link
Contributor

@dschaefer2 I have not had time to look at this issue yet (again), but I haven't forgotten about it. :)

@grynspan
Copy link
Contributor

grynspan commented Oct 8, 2024

Sorry for the delay. I was able to reproduce the issue. I am looking at the generated assembly and I think I have a rough understanding of what's happening:

  1. The code in question is in main.swift, which has special handling in the Swift compiler as the entry point into a target's code. The compiler expects that this code will be executed top-to-bottom, so it's not going to emit any one-time initializer thunks for the globals in this file.
  2. XCTest compiles this code into a dylib (.xctest bundle) where the implicit main function is just another exported symbol.
  3. At runtime, XCTest doesn't call main() (because why would it? this is a dylib) and instead enumerates all the Objective-C classes looking for XCTestCase subclasses to run.
  4. It runs testSuccess(), which then calls into a global declared in main.swift, which the compiler assumes has already been set up because main() should have been called.
  5. Splat.

Short-term workaround for @dabrahams: rename main.swift to something else if you can so that you don't get the magic "main file" behaviour. You can still use the @main struct Main dance to make sure there's an emitted main function for when the code's built as an executable.

@dschaefer2 I'm not 100% sure what the best approach is here for a fix. The compiler's not wrong from its point of view, nor is XCTest. Perhaps we need to pass some flag to the compiler on Darwin saying "treat main.swift the same as any other file"? I think such a flag exists, but I don't know what it is offhand.

@grynspan
Copy link
Contributor

grynspan commented Oct 8, 2024

Applying this diff allowed the test to pass locally, which I think confirms my hypothesis:

diff --git a/examples/expr/main.swift b/examples/expr/expr.swift
similarity index 88%
rename from examples/expr/main.swift
rename to examples/expr/expr.swift
index 3a0908a..461834b 100644
--- a/examples/expr/main.swift
+++ b/examples/expr/expr.swift
@@ -74,6 +74,8 @@ func parse(_ input: String) throws -> ArithmeticExpression {
   return try parser.endParsing()
 }
 
+@main struct Main {
+  static func main() {
     if CommandLine.argc != 2 {
       print("Pass the expression to be parsed as a quoted argument.")
     } else {
@@ -85,3 +87,5 @@ if CommandLine.argc != 2 {
         print("Error during parsing: \(error)")
       }
     }
+  }
+}

@dabrahams
Copy link
Contributor Author

rename main.swift to something else if you can so that you don't get the magic "main file" behaviour. You can still use the @main struct Main dance to make sure there's an emitted main function for when the code's built as an executable.

@grynspan thanks. IIRC there were other bugs with @main struct Main (not working reliably on Windows?) that were preventing me from doing this, but they may have been fixed. FYI I probably won't touch this code soon; that project is way down the stack for me at the moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug swift test Changes impacting `swift test` tool
Projects
None yet
Development

No branches or pull requests

5 participants