From f72f6296947bc45e6960ee82d7c65a457c166ef3 Mon Sep 17 00:00:00 2001
From: Jonathan Grynspan <jgrynspan@apple.com>
Date: Tue, 8 Oct 2024 18:46:00 -0400
Subject: [PATCH] Store test content in a custom metadata section.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This PR uses the experimental symbol linkage margers feature in the Swift
compiler to emit metadata about tests (and exit tests) into a dedicated section
of the test executable being built. At runtime, we discover that section and
read out the tests from it.

This has several benefits over our current model, which involves walking Swift's
type metadata table looking for types that conform to a protocol:

1. We don't need to define that protocol as public API in Swift Testing,
1. We don't need to emit type metadata (much larger than what we really need)
   for every test function,
1. We don't need to duplicate a large chunk of the Swift ABI sources in order to
   walk the type metadata table correctly, and
1. Almost all the new code is written in Swift, whereas the code it is intended
   to replace could not be fully represented in Swift and needed to be written
   in C++.

The change also opens up the possibility of supporting generic types in the
future because we can emit metadata without needing to emit a nested type (which
is not always valid in a generic context.) That's a "future direction" and not
covered by this PR specifically.

I've defined a layout for entries in the new `swift5_tests` section that should
be flexible enough for us in the short-to-medium term and which lets us define
additional arbitrary test content record types. The layout of this section is
covered in depth in the new [TestContent.md](Documentation/ABI/TestContent.md)
article.

This functionality is only available if a test target enables the experimental
`"SymbolLinkageMarkers"` feature. We continue to emit protocol-conforming types
for now—that code will be removed if and when the experimental feature is
properly supported (modulo us adopting relevant changes to the feature's API.)

https://github.com/swiftlang/swift-testing/issues/735
https://github.com/swiftlang/swift/issues/76698
https://github.com/swiftlang/swift/pull/78411
---
 Documentation/Porting.md                      |  6 ++
 Package.swift                                 |  2 +
 Sources/Testing/Discovery+Platform.swift      | 12 ++-
 Sources/Testing/ExitTests/ExitTest.swift      |  2 +
 Sources/Testing/Test+Discovery+Legacy.swift   |  2 +
 Sources/Testing/Test+Discovery.swift          |  2 +
 Sources/TestingMacros/CMakeLists.txt          |  2 +
 Sources/TestingMacros/ConditionMacro.swift    | 51 +++++++++-
 .../TestingMacros/SuiteDeclarationMacro.swift | 46 ++++++++-
 .../IntegerLiteralExprSyntaxAdditions.swift   | 18 ++++
 .../Additions/TokenSyntaxAdditions.swift      | 11 +++
 .../Support/TestContentGeneration.swift       | 91 ++++++++++++++++++
 .../TestingMacros/TestDeclarationMacro.swift  | 93 ++++++++++++-------
 Sources/_TestingInternals/Discovery.cpp       | 12 +++
 Sources/_TestingInternals/include/Discovery.h |  2 +
 .../TestDeclarationMacroTests.swift           |  4 +-
 Tests/TestingTests/MiscellaneousTests.swift   | 17 ++++
 cmake/modules/shared/CompilerSettings.cmake   |  2 +
 18 files changed, 335 insertions(+), 40 deletions(-)
 create mode 100644 Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift
 create mode 100644 Sources/TestingMacros/Support/TestContentGeneration.swift

diff --git a/Documentation/Porting.md b/Documentation/Porting.md
index ce179d53d..0cc6bf70b 100644
--- a/Documentation/Porting.md
+++ b/Documentation/Porting.md
@@ -145,8 +145,10 @@ to load that information:
 +  let resourceName: Str255 = switch kind {
 +  case .testContent:
 +    "__swift5_tests"
++#if !SWT_NO_LEGACY_TEST_DISCOVERY
 +  case .typeMetadata:
 +    "__swift5_types"
++#endif
 +  }
 +
 +  let oldRefNum = CurResFile()
@@ -219,15 +221,19 @@ diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals
 +#elif defined(macintosh)
 +extern "C" const char testContentSectionBegin __asm__("...");
 +extern "C" const char testContentSectionEnd __asm__("...");
++#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 +extern "C" const char typeMetadataSectionBegin __asm__("...");
 +extern "C" const char typeMetadataSectionEnd __asm__("...");
++#endif
  #else
  #warning Platform-specific implementation missing: Runtime test discovery unavailable (static)
  static const char testContentSectionBegin = 0;
  static const char& testContentSectionEnd = testContentSectionBegin;
+ #if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
  static const char typeMetadataSectionBegin = 0;
  static const char& typeMetadataSectionEnd = testContentSectionBegin;
  #endif
+ #endif
 ```
 
 These symbols must have unique addresses corresponding to the first byte of the
diff --git a/Package.swift b/Package.swift
index 4a22b98e2..57c6ea42c 100644
--- a/Package.swift
+++ b/Package.swift
@@ -156,6 +156,8 @@ extension Array where Element == PackageDescription.SwiftSetting {
       .enableExperimentalFeature("AccessLevelOnImport"),
       .enableUpcomingFeature("InternalImportsByDefault"),
 
+      .enableExperimentalFeature("SymbolLinkageMarkers"),
+
       .define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),
 
       .define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
diff --git a/Sources/Testing/Discovery+Platform.swift b/Sources/Testing/Discovery+Platform.swift
index 1615c2384..9e7a8356f 100644
--- a/Sources/Testing/Discovery+Platform.swift
+++ b/Sources/Testing/Discovery+Platform.swift
@@ -24,8 +24,10 @@ struct SectionBounds: Sendable {
     /// The test content metadata section.
     case testContent
 
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
     /// The type metadata section.
     case typeMetadata
+#endif
   }
 
   /// All section bounds of the given kind found in the current process.
@@ -85,7 +87,9 @@ private let _startCollectingSectionBounds: Void = {
       }
     }
     findSectionBounds(forSectionNamed: "__DATA_CONST", "__swift5_tests", ofKind: .testContent)
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
     findSectionBounds(forSectionNamed: "__TEXT", "__swift5_types", ofKind: .typeMetadata)
+#endif
   }
 
 #if _runtime(_ObjC)
@@ -94,7 +98,7 @@ private let _startCollectingSectionBounds: Void = {
   }
 #else
   _dyld_register_func_for_add_image { mh, _ in
-    addSectionBounds(from: mh)
+    addSectionBounds(from: mh!)
   }
 #endif
 }()
@@ -144,8 +148,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> [SectionBounds] {
       let range = switch context.pointee.kind {
       case .testContent:
         sections.swift5_tests
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
       case .typeMetadata:
         sections.swift5_type_metadata
+#endif
       }
       let start = UnsafeRawPointer(bitPattern: range.start)
       let size = Int(clamping: range.length)
@@ -234,8 +240,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> [SectionBounds] {
   let sectionName = switch kind {
   case .testContent:
     ".sw5test"
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
   case .typeMetadata:
     ".sw5tymd"
+#endif
   }
   return HMODULE.all.compactMap { _findSection(named: sectionName, in: $0) }
 }
@@ -267,8 +275,10 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> CollectionOfOne<Secti
   let (sectionBegin, sectionEnd) = switch kind {
   case .testContent:
     SWTTestContentSectionBounds
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
   case .typeMetadata:
     SWTTypeMetadataSectionBounds
+#endif
   }
   let buffer = UnsafeRawBufferPointer(start: sectionBegin, count: max(0, sectionEnd - sectionBegin))
   let sb = SectionBounds(imageAddress: nil, buffer: buffer)
diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift
index b453a6b2d..dbcae012c 100644
--- a/Sources/Testing/ExitTests/ExitTest.swift
+++ b/Sources/Testing/ExitTests/ExitTest.swift
@@ -251,6 +251,7 @@ extension ExitTest {
       }
     }
 
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
     if result == nil {
       // Call the legacy lookup function that discovers tests embedded in types.
       result = types(withNamesContaining: exitTestContainerTypeNameMagic).lazy
@@ -258,6 +259,7 @@ extension ExitTest {
         .first { $0.__id == id }
         .map { ExitTest(__identifiedBy: $0.__id, body: $0.__body) }
     }
+#endif
 
     return result
   }
diff --git a/Sources/Testing/Test+Discovery+Legacy.swift b/Sources/Testing/Test+Discovery+Legacy.swift
index c90e8c590..4612529b7 100644
--- a/Sources/Testing/Test+Discovery+Legacy.swift
+++ b/Sources/Testing/Test+Discovery+Legacy.swift
@@ -10,6 +10,7 @@
 
 private import _TestingInternals
 
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
 /// A protocol describing a type that contains tests.
 ///
 /// - Warning: This protocol is used to implement the `@Test` macro. Do not use
@@ -64,3 +65,4 @@ func types(withNamesContaining nameSubstring: String) -> some Sequence<Any.Type>
       .withMemoryRebound(to: Any.Type.self) { Array($0) }
   }
 }
+#endif
diff --git a/Sources/Testing/Test+Discovery.swift b/Sources/Testing/Test+Discovery.swift
index 751a7de85..b6e84a852 100644
--- a/Sources/Testing/Test+Discovery.swift
+++ b/Sources/Testing/Test+Discovery.swift
@@ -55,6 +55,7 @@ extension Test: TestContent {
         }
       }
 
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
       if discoveryMode != .newOnly && generators.isEmpty {
         generators += types(withNamesContaining: testContainerTypeNameMagic).lazy
           .compactMap { $0 as? any __TestContainer.Type }
@@ -62,6 +63,7 @@ extension Test: TestContent {
             { @Sendable in await type.__tests }
           }
       }
+#endif
 
       // *Now* we call all the generators and return their results.
       // Reduce into a set rather than an array to deduplicate tests that were
diff --git a/Sources/TestingMacros/CMakeLists.txt b/Sources/TestingMacros/CMakeLists.txt
index acf09f339..4a42d9667 100644
--- a/Sources/TestingMacros/CMakeLists.txt
+++ b/Sources/TestingMacros/CMakeLists.txt
@@ -87,6 +87,7 @@ target_sources(TestingMacros PRIVATE
   Support/Additions/DeclGroupSyntaxAdditions.swift
   Support/Additions/EditorPlaceholderExprSyntaxAdditions.swift
   Support/Additions/FunctionDeclSyntaxAdditions.swift
+  Support/Additions/IntegerLiteralExprSyntaxAdditions.swift
   Support/Additions/MacroExpansionContextAdditions.swift
   Support/Additions/TokenSyntaxAdditions.swift
   Support/Additions/TriviaPieceAdditions.swift
@@ -103,6 +104,7 @@ target_sources(TestingMacros PRIVATE
   Support/DiagnosticMessage+Diagnosing.swift
   Support/SourceCodeCapturing.swift
   Support/SourceLocationGeneration.swift
+  Support/TestContentGeneration.swift
   TagMacro.swift
   TestDeclarationMacro.swift
   TestingMacrosMain.swift)
diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift
index 346cb68bf..aa81b4765 100644
--- a/Sources/TestingMacros/ConditionMacro.swift
+++ b/Sources/TestingMacros/ConditionMacro.swift
@@ -435,11 +435,57 @@ extension ExitTestConditionMacro {
 
     // Create a local type that can be discovered at runtime and which contains
     // the exit test body.
-    let enumName = context.makeUniqueName("__🟠$exit_test_body__")
+    let accessorName = context.makeUniqueName("")
+    let outValueArgumentName = context.makeUniqueName("outValue")
+    let hintArgumentName = context.makeUniqueName("hint")
+    let idLocalName = context.makeUniqueName("id")
+    decls.append(
+      """
+      #if hasFeature(SymbolLinkageMarkers)
+      func \(accessorName)(_ \(outValueArgumentName): UnsafeMutableRawPointer, _ \(hintArgumentName): UnsafeRawPointer?) -> Bool {
+        let \(idLocalName) = \(exitTestIDExpr)
+        if let \(hintArgumentName) = \(hintArgumentName)?.load(as: Testing.__ExitTest.ID.self),
+          \(hintArgumentName) != \(idLocalName) {
+          return false
+        }
+        \(outValueArgumentName).initializeMemory(
+          as: Testing.__ExitTest.self,
+          to: .init(
+            __identifiedBy: \(idLocalName),
+            body: \(bodyThunkName)
+          )
+        )
+        return true
+      }
+      #endif
+      """
+    )
+
+    let enumName = context.makeUniqueName("")
+    let sectionContent = makeTestContentRecordDecl(
+      named: .identifier("__sectionContent"),
+      in: TypeSyntax(IdentifierTypeSyntax(name: enumName)),
+      ofKind: .exitTest,
+      accessingWith: accessorName
+    )
+    decls.append(
+      """
+      #if hasFeature(SymbolLinkageMarkers)
+      @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
+      enum \(enumName) {
+        \(sectionContent)
+      }
+      #endif
+      """
+    )
+
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
+    // Emit a legacy type declaration if SymbolLinkageMarkers is off.
+    let legacyEnumName = context.makeUniqueName("__🟠$exit_test_body__")
     decls.append(
       """
       @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
-      enum \(enumName): Testing.__ExitTestContainer, Sendable {
+      enum \(legacyEnumName): Testing.__ExitTestContainer, Sendable {
         static var __id: Testing.__ExitTest.ID {
           \(exitTestIDExpr)
         }
@@ -449,6 +495,7 @@ extension ExitTestConditionMacro {
       }
       """
     )
+#endif
 
     arguments[trailingClosureIndex].expression = ExprSyntax(
       ClosureExprSyntax {
diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift
index c9fb6bb08..9aa3bfaf3 100644
--- a/Sources/TestingMacros/SuiteDeclarationMacro.swift
+++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift
@@ -127,6 +127,39 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable {
     // Parse the @Suite attribute.
     let attributeInfo = AttributeInfo(byParsing: suiteAttribute, on: declaration, in: context)
 
+    let accessorName = context.makeUniqueName("")
+    result.append(
+      """
+      #if hasFeature(SymbolLinkageMarkers)
+      @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.")
+      private static let \(accessorName): @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer?) -> Bool = {
+        $0.initializeMemory(as: (@Sendable () async -> Testing.Test).self) { @Sendable () async in
+          .__type(
+            \(declaration.type.trimmed).self,
+            \(raw: attributeInfo.functionArgumentList(in: context))
+          )
+        }
+        _ = $1 // Ignored.
+        return true
+      }
+      #endif
+      """
+    )
+
+    let sectionContentName = context.makeUniqueName("")
+    result.append(
+      makeTestContentRecordDecl(
+        named: sectionContentName,
+        in: declaration.type,
+        ofKind: .testDeclaration,
+        accessingWith: accessorName,
+        context: 1 << 0 /* suite decl */
+      )
+    )
+
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
+    // Emit a legacy type declaration if SymbolLinkageMarkers is off.
+    //
     // The emitted type must be public or the compiler can optimize it away
     // (since it is not actually used anywhere that the compiler can see.)
     //
@@ -142,17 +175,24 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable {
       """
       @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
       enum \(enumName): Testing.__TestContainer {
-        static var __tests: [Testing.Test] {
-          get async {[
+        private static var __test: Testing.Test {
+          get async {
             .__type(
               \(declaration.type.trimmed).self,
               \(raw: attributeInfo.functionArgumentList(in: context))
             )
-          ]}
+          }
+        }
+
+        static var __tests: [Testing.Test] {
+          get async {
+            [await __test]
+          }
         }
       }
       """
     )
+#endif
 
     return result
   }
diff --git a/Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift
new file mode 100644
index 000000000..e2310b44f
--- /dev/null
+++ b/Sources/TestingMacros/Support/Additions/IntegerLiteralExprSyntaxAdditions.swift
@@ -0,0 +1,18 @@
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2024 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
+//
+
+import SwiftSyntax
+
+extension IntegerLiteralExprSyntax {
+  init(_ value: some BinaryInteger, radix: IntegerLiteralExprSyntax.Radix = .decimal) {
+    let stringValue = "\(radix.literalPrefix)\(String(value, radix: radix.size))"
+    self.init(literal: .integerLiteral(stringValue))
+  }
+}
diff --git a/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift b/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift
index 12e6abb24..39f21b733 100644
--- a/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift
+++ b/Sources/TestingMacros/Support/Additions/TokenSyntaxAdditions.swift
@@ -47,3 +47,14 @@ extension TokenSyntax {
     return nil
   }
 }
+
+  /// The `static` keyword, if `typeName` is not `nil`.
+  ///
+  /// - Parameters:
+  ///   - typeName: The name of the type containing the macro being expanded.
+  ///
+  /// - Returns: A token representing the `static` keyword, or one representing
+  ///   nothing if `typeName` is `nil`.
+  func staticKeyword(for typeName: TypeSyntax?) -> TokenSyntax {
+    (typeName != nil) ? .keyword(.static) : .unknown("")
+  }
diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift
new file mode 100644
index 000000000..1d845cdb8
--- /dev/null
+++ b/Sources/TestingMacros/Support/TestContentGeneration.swift
@@ -0,0 +1,91 @@
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2024 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
+//
+
+import SwiftSyntax
+import SwiftSyntaxMacros
+
+/// An enumeration representing the different kinds of test content known to the
+/// testing library.
+///
+/// When adding cases to this enumeration, be sure to also update the
+/// corresponding enumeration in TestContent.md.
+enum TestContentKind: UInt32 {
+  /// A test or suite declaration.
+  case testDeclaration = 0x74657374
+
+  /// An exit test.
+  case exitTest = 0x65786974
+
+  /// This kind value as a comment (`/* 'abcd' */`) if it looks like it might be
+  /// a [FourCC](https://en.wikipedia.org/wiki/FourCC) value, or `nil` if not.
+  var commentRepresentation: Trivia? {
+    return withUnsafeBytes(of: rawValue.bigEndian) { bytes in
+      if bytes.allSatisfy(Unicode.ASCII.isASCII) {
+        let characters = String(decoding: bytes, as: Unicode.ASCII.self)
+        let allAlphanumeric = characters.allSatisfy { $0.isLetter || $0.isWholeNumber }
+        if allAlphanumeric {
+          return .blockComment("/* '\(characters)' */")
+        }
+      }
+      return nil
+    }
+  }
+}
+
+/// Make a test content record that can be discovered at runtime by the testing
+/// library.
+///
+/// - Parameters:
+///   - name: The name of the record declaration to use in Swift source. The
+///     value of this argument should be unique in the context in which the
+///     declaration will be emitted.
+///   - typeName: The name of the type enclosing the resulting declaration, or
+///     `nil` if it will not be emitted into a type's scope.
+///   - kind: The kind of test content record being emitted.
+///   - accessorName: The Swift name of an `@convention(c)` function to emit
+///     into the resulting record.
+///   - context: A value to emit as the `context` field of the test content
+///     record.
+///
+/// - Returns: A variable declaration that, when emitted into Swift source, will
+///   cause the linker to emit data in a location that is discoverable at
+///   runtime.
+func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? = nil, ofKind kind: TestContentKind, accessingWith accessorName: TokenSyntax, context: UInt32 = 0) -> DeclSyntax {
+  let kindExpr = IntegerLiteralExprSyntax(kind.rawValue, radix: .hex)
+  let kindComment = kind.commentRepresentation.map { .space + $0 } ?? Trivia()
+  let contextExpr = if context == 0 {
+    IntegerLiteralExprSyntax(0)
+  } else {
+    IntegerLiteralExprSyntax(context, radix: .binary)
+  }
+
+  return """
+  #if hasFeature(SymbolLinkageMarkers)
+  #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) || os(visionOS)
+  @_section("__DATA_CONST,__swift5_tests")
+  #elseif os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || os(WASI)
+  @_section("swift5_tests")
+  #elseif os(Windows)
+  @_section(".sw5test$B")
+  #else
+  @__testing(warning: "Platform-specific implementation missing: test content section name unavailable")
+  #endif
+  @_used
+  @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.")
+  private \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = (
+    \(kindExpr),\(kindComment)
+    0,
+    \(accessorName),
+    \(contextExpr),
+    0
+  )
+  #endif
+  """
+}
diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift
index 1a3f2c448..8b02a9fde 100644
--- a/Sources/TestingMacros/TestDeclarationMacro.swift
+++ b/Sources/TestingMacros/TestDeclarationMacro.swift
@@ -172,17 +172,6 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
     return FunctionParameterClauseSyntax(parameters: parameterList)
   }
 
-  /// The `static` keyword, if `typeName` is not `nil`.
-  ///
-  /// - Parameters:
-  ///   - typeName: The name of the type containing the macro being expanded.
-  ///
-  /// - Returns: A token representing the `static` keyword, or one representing
-  ///   nothing if `typeName` is `nil`.
-  private static func _staticKeyword(for typeName: TypeSyntax?) -> TokenSyntax {
-    (typeName != nil) ? .keyword(.static) : .unknown("")
-  }
-
   /// Create a thunk function with a normalized signature that calls a
   /// developer-supplied test function.
   ///
@@ -340,7 +329,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
     let thunkName = context.makeUniqueName(thunking: functionDecl)
     let thunkDecl: DeclSyntax = """
     @available(*, deprecated, message: "This function is an implementation detail of the testing library. Do not use it directly.")
-    @Sendable private \(_staticKeyword(for: typeName)) func \(thunkName)\(thunkParamsExpr) async throws -> Void {
+    @Sendable private \(staticKeyword(for: typeName)) func \(thunkName)\(thunkParamsExpr) async throws -> Void {
       \(thunkBody)
     }
     """
@@ -405,16 +394,14 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
 
     // Create the expression that returns the Test instance for the function.
     var testsBody: CodeBlockItemListSyntax = """
-    return [
-      .__function(
-        named: \(literal: functionDecl.completeName.trimmedDescription),
-        in: \(typeNameExpr),
-        xcTestCompatibleSelector: \(selectorExpr ?? "nil"),
-        \(raw: attributeInfo.functionArgumentList(in: context)),
-        parameters: \(raw: functionDecl.testFunctionParameterList),
-        testFunction: \(thunkDecl.name)
-      )
-    ]
+    return .__function(
+      named: \(literal: functionDecl.completeName.trimmedDescription),
+      in: \(typeNameExpr),
+      xcTestCompatibleSelector: \(selectorExpr ?? "nil"),
+      \(raw: attributeInfo.functionArgumentList(in: context)),
+      parameters: \(raw: functionDecl.testFunctionParameterList),
+      testFunction: \(thunkDecl.name)
+    )
     """
 
     // If this function has arguments, then it can only be referenced (let alone
@@ -430,16 +417,14 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
       result.append(
         """
         @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.")
-        private \(_staticKeyword(for: typeName)) nonisolated func \(unavailableTestName)() async -> [Testing.Test] {
-          [
-            .__function(
-              named: \(literal: functionDecl.completeName.trimmedDescription),
-              in: \(typeNameExpr),
-              xcTestCompatibleSelector: \(selectorExpr ?? "nil"),
-              \(raw: attributeInfo.functionArgumentList(in: context)),
-              testFunction: {}
-            )
-          ]
+        private \(staticKeyword(for: typeName)) nonisolated func \(unavailableTestName)() async -> Testing.Test {
+          .__function(
+            named: \(literal: functionDecl.completeName.trimmedDescription),
+            in: \(typeNameExpr),
+            xcTestCompatibleSelector: \(selectorExpr ?? "nil"),
+            \(raw: attributeInfo.functionArgumentList(in: context)),
+            testFunction: {}
+          )
         }
         """
       )
@@ -454,6 +439,41 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
       )
     }
 
+    let accessorName = context.makeUniqueName(thunking: functionDecl)
+    result.append(
+      """
+      #if hasFeature(SymbolLinkageMarkers)
+      @available(*, deprecated, message: "This property is an implementation detail of the testing library. Do not use it directly.")
+      private \(staticKeyword(for: typeName)) let \(accessorName): @convention(c) (UnsafeMutableRawPointer, UnsafeRawPointer?) -> Bool = {
+        $0.initializeMemory(as: (@Sendable () async -> Testing.Test).self) { @Sendable () async in
+          \(raw: testsBody)
+        }
+        _ = $1 // Ignored.
+        return true
+      }
+      #endif
+      """
+    )
+
+    var flags = UInt32(0)
+    if attributeInfo.hasFunctionArguments {
+      flags |= 1 << 1 /* is parameterized */
+    }
+
+    let sectionContentName = context.makeUniqueName(thunking: functionDecl)
+    result.append(
+      makeTestContentRecordDecl(
+        named: sectionContentName,
+        in: typeName,
+        ofKind: .testDeclaration,
+        accessingWith: accessorName,
+        context: flags
+      )
+    )
+
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
+    // Emit a legacy type declaration if SymbolLinkageMarkers is off.
+    //
     // The emitted type must be public or the compiler can optimize it away
     // (since it is not actually used anywhere that the compiler can see.)
     //
@@ -469,14 +489,21 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
       """
       @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
       enum \(enumName): Testing.__TestContainer {
-        static var __tests: [Testing.Test] {
+        private static var __test: Testing.Test {
           get async {
             \(raw: testsBody)
           }
         }
+
+        static var __tests: [Testing.Test] {
+          get async {
+            [await __test]
+          }
+        }
       }
       """
     )
+#endif
 
     return result
   }
diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp
index f66eaf874..bd3084837 100644
--- a/Sources/_TestingInternals/Discovery.cpp
+++ b/Sources/_TestingInternals/Discovery.cpp
@@ -10,11 +10,13 @@
 
 #include "Discovery.h"
 
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 #include <cstdlib>
 #include <cstdint>
 #include <cstring>
 #include <type_traits>
 #include <vector>
+#endif
 
 #if defined(SWT_NO_DYNAMIC_LINKING)
 #pragma mark - Statically-linked section bounds
@@ -22,30 +24,39 @@
 #if defined(__APPLE__)
 extern "C" const char testContentSectionBegin __asm("section$start$__DATA_CONST$__swift5_tests");
 extern "C" const char testContentSectionEnd __asm("section$end$__DATA_CONST$__swift5_tests");
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 extern "C" const char typeMetadataSectionBegin __asm__("section$start$__TEXT$__swift5_types");
 extern "C" const char typeMetadataSectionEnd __asm__("section$end$__TEXT$__swift5_types");
+#endif
 #elif defined(__wasi__)
 extern "C" const char testContentSectionBegin __asm__("__start_swift5_tests");
 extern "C" const char testContentSectionEnd __asm__("__stop_swift5_tests");
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 extern "C" const char typeMetadataSectionBegin __asm__("__start_swift5_type_metadata");
 extern "C" const char typeMetadataSectionEnd __asm__("__stop_swift5_type_metadata");
+#endif
 #else
 #warning Platform-specific implementation missing: Runtime test discovery unavailable (static)
 static const char testContentSectionBegin = 0;
 static const char& testContentSectionEnd = testContentSectionBegin;
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 static const char typeMetadataSectionBegin = 0;
 static const char& typeMetadataSectionEnd = typeMetadataSectionBegin;
 #endif
+#endif
 
 const void *_Nonnull const SWTTestContentSectionBounds[2] = {
   &testContentSectionBegin, &testContentSectionEnd
 };
 
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 const void *_Nonnull const SWTTypeMetadataSectionBounds[2] = {
   &typeMetadataSectionBegin, &typeMetadataSectionEnd
 };
 #endif
+#endif
 
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 #pragma mark - Swift ABI
 
 #if defined(__PTRAUTH_INTRINSICS__)
@@ -232,3 +243,4 @@ void **swt_copyTypesWithNamesContaining(const void *sectionBegin, size_t section
   *outCount = resultCount;
   return result;
 }
+#endif
diff --git a/Sources/_TestingInternals/include/Discovery.h b/Sources/_TestingInternals/include/Discovery.h
index cbcfc2d79..044b998f3 100644
--- a/Sources/_TestingInternals/include/Discovery.h
+++ b/Sources/_TestingInternals/include/Discovery.h
@@ -82,6 +82,7 @@ SWT_EXTERN const void *_Nonnull const SWTTestContentSectionBounds[2];
 ///   when Swift files import the `_TestingInternals` C++ module.
 SWT_EXTERN const void *_Nonnull const SWTTypeMetadataSectionBounds[2];
 
+#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY)
 #pragma mark - Legacy test discovery
 
 /// Copy all types known to Swift found in the given type metadata section with
@@ -103,6 +104,7 @@ SWT_EXTERN void *_Nonnull *_Nullable swt_copyTypesWithNamesContaining(
   const char *nameSubstring,
   size_t *outCount
 ) SWT_SWIFT_NAME(swt_copyTypes(in:_:withNamesContaining:count:));
+#endif
 
 SWT_ASSUME_NONNULL_END
 
diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift
index a77acfea1..1d7dbbdcf 100644
--- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift
+++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift
@@ -402,7 +402,9 @@ struct TestDeclarationMacroTests {
   func differentFunctionTypes(input: String, expectedTypeName: String?, otherCode: String?) throws {
     let (output, _) = try parse(input)
 
-    #expect(output.contains("__TestContainer"))
+    withKnownIssue("https://github.com/swiftlang/swift-syntax/issues/2923") {
+      #expect(output.contains("@_section"))
+    }
     if let expectedTypeName {
       #expect(output.contains(expectedTypeName))
     }
diff --git a/Tests/TestingTests/MiscellaneousTests.swift b/Tests/TestingTests/MiscellaneousTests.swift
index 49df6cc6e..60e781cf9 100644
--- a/Tests/TestingTests/MiscellaneousTests.swift
+++ b/Tests/TestingTests/MiscellaneousTests.swift
@@ -654,4 +654,21 @@ struct MiscellaneousTests {
     }
   }
 #endif
+
+#if !SWT_NO_LEGACY_TEST_DISCOVERY
+  @Test("Legacy test discovery finds the same number of tests") func discoveredTestCount() async {
+    let oldFlag = Environment.variable(named: "SWT_USE_LEGACY_TEST_DISCOVERY")
+    defer {
+      Environment.setVariable(oldFlag, named: "SWT_USE_LEGACY_TEST_DISCOVERY")
+    }
+
+    Environment.setVariable("1", named: "SWT_USE_LEGACY_TEST_DISCOVERY")
+    let testsWithOldCode = await Array(Test.all).count
+
+    Environment.setVariable("0", named: "SWT_USE_LEGACY_TEST_DISCOVERY")
+    let testsWithNewCode = await Array(Test.all).count
+
+    #expect(testsWithOldCode == testsWithNewCode)
+  }
+#endif
 }
diff --git a/cmake/modules/shared/CompilerSettings.cmake b/cmake/modules/shared/CompilerSettings.cmake
index 49e2579fe..13fa101e2 100644
--- a/cmake/modules/shared/CompilerSettings.cmake
+++ b/cmake/modules/shared/CompilerSettings.cmake
@@ -16,6 +16,8 @@ add_compile_options(
 add_compile_options(
   "SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-upcoming-feature -Xfrontend ExistentialAny>"
   "SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-upcoming-feature -Xfrontend InternalImportsByDefault>")
+add_compile_options(
+  "SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-experimental-feature -Xfrontend SymbolLinkageMarkers>")
 
 # Platform-specific definitions.
 if(APPLE)