diff --git a/Documentation/EnvironmentVariables.md b/Documentation/EnvironmentVariables.md index a73ffdc65..b73b6fa9c 100644 --- a/Documentation/EnvironmentVariables.md +++ b/Documentation/EnvironmentVariables.md @@ -63,4 +63,3 @@ names prefixed with `SWT_`. | `HOME`[\*](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap08.html) | `String` | Used to determine the user's home directory. | | `SIMULATOR_RUNTIME_BUILD_VERSION`\* | `String` | Used when running in the iOS (etc.) Simulator to determine the simulator's version. | | `SIMULATOR_RUNTIME_VERSION`\* | `String` | Used when running in the iOS (etc.) Simulator to determine the simulator's version. | -| `SWT_USE_LEGACY_TEST_DISCOVERY` | `Bool` | Used to explicitly enable or disable legacy test discovery. | diff --git a/Documentation/Porting.md b/Documentation/Porting.md index 4773e2823..2e60ebe05 100644 --- a/Documentation/Porting.md +++ b/Documentation/Porting.md @@ -148,10 +148,6 @@ 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() @@ -193,7 +189,7 @@ to load that information: ``` You will also need to update the `makeTestContentRecordDecl()` function in the -`TestingMacros` target to emit the correct `@_section` attribute for your +`TestingMacros` target to emit the correct `@section` attribute for your platform. If your platform uses the ELF image format and supports the `dl_iterate_phdr()` function, add it to the existing `#elseif os(Linux) || ...` case. Otherwise, add a new case for your platform: @@ -203,7 +199,7 @@ case. Otherwise, add a new case for your platform: +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift // ... + #elseif os(Classic) -+ @_section(".rsrc,swft,__swift5_tests") ++ @section(".rsrc,swft,__swift5_tests") #else @__testing(warning: "Platform-specific implementation missing: test content section name unavailable") #endif @@ -214,14 +210,18 @@ directly into test authors' test targets, so you will not be able to use compiler conditionals defined in the Swift Testing package (including those that start with `"SWT_"`). +> [!NOTE] +> We are not using `objectFormat()` yet to maintain compatibility with the Swift +> 6.2 toolchain. We will migrate to `objectFormat()` when we drop Swift 6.2 +> toolchain support (presumably after Swift 6.3 ships). + ## Runtime test discovery with static linkage If your platform does not support dynamic linking and loading, you will need to use static linkage instead. Define the `"SWT_NO_DYNAMIC_LINKING"` compiler conditional for your platform in both `Package.swift` and -`CompilerSettings.cmake`, then define the symbols `_testContentSectionBegin`, -`_testContentSectionEnd`, `_typeMetadataSectionBegin`, and -`_typeMetadataSectionEnd` in `SectionBounds.swift`: +`CompilerSettings.cmake`, then define the symbols `_testContentSectionBegin` and +`_testContentSectionEnd` in `SectionBounds.swift`: ```diff --- a/Sources/_TestDiscovery/SectionBounds.swift @@ -230,18 +230,10 @@ conditional for your platform in both `Package.swift` and +#elseif os(Classic) +@_silgen_name(raw: "...") private nonisolated(unsafe) var _testContentSectionBegin: _SectionBound +@_silgen_name(raw: "...") private nonisolated(unsafe) var _testContentSectionEnd: _SectionBound -+#if !SWT_NO_LEGACY_TEST_DISCOVERY -+@_silgen_name(raw: "...") private nonisolated(unsafe) var _typeMetadataSectionBegin: _SectionBound -+@_silgen_name(raw: "...") private nonisolated(unsafe) var _typeMetadataSectionEnd: _SectionBound -+#endif #else #warning("Platform-specific implementation missing: Runtime test discovery unavailable (static)") private nonisolated(unsafe) let _testContentSectionBegin = UnsafeMutableRawPointer.allocate(byteCount: 1, alignment: 16) private nonisolated(unsafe) let _testContentSectionEnd = _testContentSectionBegin - #if !SWT_NO_LEGACY_TEST_DISCOVERY - private nonisolated(unsafe) let _typeMetadataSectionBegin = UnsafeMutableRawPointer.allocate(byteCount: 1, alignment: 16) - private nonisolated(unsafe) let _typeMetadataSectionEnd = _typeMetadataSectionBegin - #endif #endif // ... ``` diff --git a/Package.swift b/Package.swift index fcac26bca..f19ae6f97 100644 --- a/Package.swift +++ b/Package.swift @@ -383,11 +383,6 @@ extension Array where Element == PackageDescription.SwiftSetting { .enableUpcomingFeature("MemberImportVisibility"), - // This setting is enabled in the package, but not in the toolchain build - // (via CMake). Enabling it is dependent on acceptance of the @section - // proposal via Swift Evolution. - .enableExperimentalFeature("SymbolLinkageMarkers"), - .enableUpcomingFeature("InferIsolatedConformances"), // When building as a package, the macro plugin always builds as an @@ -404,10 +399,15 @@ extension Array where Element == PackageDescription.SwiftSetting { .define("SWT_NO_FOUNDATION_FILE_COORDINATION", .whenEmbedded(or: .whenApple(false))), .define("SWT_NO_IMAGE_ATTACHMENTS", .whenEmbedded(or: .when(platforms: [.linux, .custom("freebsd"), .openbsd, .wasi, .android]))), - .define("SWT_NO_LEGACY_TEST_DISCOVERY", .whenEmbedded()), .define("SWT_NO_LIBDISPATCH", .whenEmbedded()), ] +#if compiler(<6.3) + result += [ + .enableExperimentalFeature("SymbolLinkageMarkers"), + ] +#endif + return result } @@ -462,7 +462,6 @@ extension Array where Element == PackageDescription.CXXSetting { .define("SWT_NO_PIPES", .whenEmbedded(or: .when(platforms: [.wasi]))), .define("SWT_NO_FOUNDATION_FILE_COORDINATION", .whenEmbedded(or: .whenApple(false))), - .define("SWT_NO_LEGACY_TEST_DISCOVERY", .whenEmbedded()), .define("SWT_NO_LIBDISPATCH", .whenEmbedded()), ] diff --git a/Sources/Testing/CMakeLists.txt b/Sources/Testing/CMakeLists.txt index b60688731..ee0fbc574 100644 --- a/Sources/Testing/CMakeLists.txt +++ b/Sources/Testing/CMakeLists.txt @@ -99,7 +99,6 @@ add_library(Testing Test.swift Test+Cancellation.swift Test+Discovery.swift - Test+Discovery+Legacy.swift Test+Macro.swift Traits/AttachmentSavingTrait.swift Traits/Bug.swift diff --git a/Sources/Testing/Discovery+Macro.swift b/Sources/Testing/Discovery+Macro.swift index 35b276efe..f3e598e13 100644 --- a/Sources/Testing/Discovery+Macro.swift +++ b/Sources/Testing/Discovery+Macro.swift @@ -34,7 +34,7 @@ public typealias __TestContentRecordAccessor = @convention(c) ( public typealias __TestContentRecord = ( kind: UInt32, reserved1: UInt32, - accessor: __TestContentRecordAccessor?, + accessor: __TestContentRecordAccessor, context: UInt, reserved2: UInt ) diff --git a/Sources/Testing/ExitTests/ExitTest.swift b/Sources/Testing/ExitTests/ExitTest.swift index da4da4c91..ee78ab4f5 100644 --- a/Sources/Testing/ExitTests/ExitTest.swift +++ b/Sources/Testing/ExitTests/ExitTest.swift @@ -420,15 +420,6 @@ extension ExitTest { } } -#if !SWT_NO_LEGACY_TEST_DISCOVERY - // Call the legacy lookup function that discovers tests embedded in types. - for record in Record.allTypeMetadataBasedTestContentRecords() { - if let exitTest = record.load(withHint: id)?.makeExitTest() { - return exitTest - } - } -#endif - return nil } } diff --git a/Sources/Testing/Test+Discovery+Legacy.swift b/Sources/Testing/Test+Discovery+Legacy.swift deleted file mode 100644 index cafc55a4e..000000000 --- a/Sources/Testing/Test+Discovery+Legacy.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023 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 -// - -#if !SWT_NO_LEGACY_TEST_DISCOVERY -@_spi(Experimental) @_spi(ForToolsIntegrationOnly) internal import _TestDiscovery - -/// A protocol describing a type that contains tests. -/// -/// - Warning: This protocol is used to implement the `@Test` macro. Do not use -/// it directly. -@_alwaysEmitConformanceMetadata -public protocol __TestContentRecordContainer { - /// The test content record associated with this container. - /// - /// - Warning: This property is used to implement the `@Test` macro. Do not - /// use it directly. - nonisolated static var __testContentRecord: __TestContentRecord { get } -} - -extension DiscoverableAsTestContent { - /// Get all test content of this type known to Swift and found in the current - /// process using the legacy discovery mechanism. - /// - /// - Returns: A sequence of instances of ``TestContentRecord``. Only test - /// content records matching this ``TestContent`` type's requirements are - /// included in the sequence. - static func allTypeMetadataBasedTestContentRecords() -> some Sequence> { - return allTypeMetadataBasedTestContentRecords { type, buffer in - guard let type = type as? any __TestContentRecordContainer.Type else { - return false - } - - buffer.withMemoryRebound(to: __TestContentRecord.self) { buffer in - buffer.baseAddress!.initialize(to: type.__testContentRecord) - } - return true - } - } -} -#endif diff --git a/Sources/Testing/Test+Discovery.swift b/Sources/Testing/Test+Discovery.swift index d7d3b8ea7..78ce4e0e4 100644 --- a/Sources/Testing/Test+Discovery.swift +++ b/Sources/Testing/Test+Discovery.swift @@ -63,51 +63,17 @@ extension Test { // defective test records.) var result = Set() - // Figure out which discovery mechanism to use. By default, we'll use both - // the legacy and new mechanisms, but we can set an environment variable - // to explicitly select one or the other. When we remove legacy support, - // we can also remove this enumeration and environment variable check. -#if !SWT_NO_LEGACY_TEST_DISCOVERY - let (useNewMode, useLegacyMode) = switch Environment.flag(named: "SWT_USE_LEGACY_TEST_DISCOVERY") { - case .none: - (true, true) - case .some(true): - (false, true) - case .some(false): - (true, false) - } -#else - let useNewMode = true -#endif - // Walk all test content and gather generator functions, then call them in // a task group and collate their results. - if useNewMode { - let generators = Generator.allTestContentRecords().lazy.compactMap { $0.load() } - await withTaskGroup { taskGroup in - for (i, generator) in generators.enumerated() { - taskGroup.addTask(name: decorateTaskName("test discovery", withAction: "loading test #\(i)")) { - await generator.rawValue() - } - } - result = await taskGroup.reduce(into: result) { $0.insert($1) } - } - } - -#if !SWT_NO_LEGACY_TEST_DISCOVERY - // Perform legacy test discovery if needed. - if useLegacyMode && result.isEmpty { - let generators = Generator.allTypeMetadataBasedTestContentRecords().lazy.compactMap { $0.load() } - await withTaskGroup { taskGroup in - for (i, generator) in generators.enumerated() { - taskGroup.addTask(name: decorateTaskName("type-based test discovery", withAction: "loading test #\(i)")) { - await generator.rawValue() - } + let generators = Generator.allTestContentRecords().lazy.compactMap { $0.load() } + await withTaskGroup(of: Self.self) { taskGroup in + for (i, generator) in generators.enumerated() { + taskGroup.addTask(name: decorateTaskName("test discovery", withAction: "loading test #\(i)")) { + await generator.rawValue() } - result = await taskGroup.reduce(into: result) { $0.insert($1) } } + result = await taskGroup.reduce(into: result) { $0.insert($1) } } -#endif return result } diff --git a/Sources/TestingMacros/ConditionMacro.swift b/Sources/TestingMacros/ConditionMacro.swift index 6ba8ff124..c42a0f9e2 100644 --- a/Sources/TestingMacros/ConditionMacro.swift +++ b/Sources/TestingMacros/ConditionMacro.swift @@ -13,10 +13,6 @@ public import SwiftSyntax import SwiftSyntaxBuilder public import SwiftSyntaxMacros -#if !hasFeature(SymbolLinkageMarkers) && SWT_NO_LEGACY_TEST_DISCOVERY -#error("Platform-specific misconfiguration: either SymbolLinkageMarkers or legacy test discovery is required to expand #expect(processExitsWith:)") -#endif - /// A protocol containing the common implementation for the expansions of the /// `#expect()` and `#require()` macros. /// @@ -494,19 +490,6 @@ extension ExitTestConditionMacro { accessingWith: .identifier("accessor") ) - // Create another local type for legacy test discovery. - var recordDecl: DeclSyntax? -#if !SWT_NO_LEGACY_TEST_DISCOVERY - let legacyEnumName = context.makeUniqueName("__🟡$") - recordDecl = """ - enum \(legacyEnumName): Testing.__TestContentRecordContainer { - nonisolated static var __testContentRecord: Testing.__TestContentRecord { - unsafe \(enumName).testContentRecord - } - } - """ -#endif - decls.append( """ @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") @@ -522,8 +505,6 @@ extension ExitTestConditionMacro { } \(testContentRecordDecl) - - \(recordDecl) } """ ) diff --git a/Sources/TestingMacros/SuiteDeclarationMacro.swift b/Sources/TestingMacros/SuiteDeclarationMacro.swift index 4bee4c30f..3c55bba62 100644 --- a/Sources/TestingMacros/SuiteDeclarationMacro.swift +++ b/Sources/TestingMacros/SuiteDeclarationMacro.swift @@ -13,10 +13,6 @@ public import SwiftSyntax import SwiftSyntaxBuilder public import SwiftSyntaxMacros -#if !hasFeature(SymbolLinkageMarkers) && SWT_NO_LEGACY_TEST_DISCOVERY -#error("Platform-specific misconfiguration: either SymbolLinkageMarkers or legacy test discovery is required to expand @Suite") -#endif - /// A type describing the expansion of the `@Suite` attribute macro. /// /// This type is used to implement the `@Suite` attribute macro. Do not use it @@ -166,21 +162,6 @@ public struct SuiteDeclarationMacro: MemberMacro, PeerMacro, Sendable { ) ) -#if !SWT_NO_LEGACY_TEST_DISCOVERY - // Emit a type that contains a reference to the test content record. - let enumName = context.makeUniqueName("__🟡$") - result.append( - """ - @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") - enum \(enumName): Testing.__TestContentRecordContainer { - nonisolated static var __testContentRecord: Testing.__TestContentRecord { - unsafe \(testContentRecordName) - } - } - """ - ) -#endif - return result } } diff --git a/Sources/TestingMacros/Support/TestContentGeneration.swift b/Sources/TestingMacros/Support/TestContentGeneration.swift index 05214d1b8..ca7990b6c 100644 --- a/Sources/TestingMacros/Support/TestContentGeneration.swift +++ b/Sources/TestingMacros/Support/TestContentGeneration.swift @@ -68,15 +68,28 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? private nonisolated \(staticKeyword(for: typeName)) let \(name): Testing.__TestContentRecord = ( \(kindExpr), \(kind.commentRepresentation) 0, - unsafe \(accessorName), + { unsafe \(accessorName)($0, $1, $2, $3) }, \(contextExpr), 0 ) """ -#if hasFeature(SymbolLinkageMarkers) +#if compiler(>=6.3) + result = """ + #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.__testing(warning: "Platform-specific implementation missing: test content section name unavailable") + #endif + @used + \(result) + """ +#else result = """ - #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) @@ -87,7 +100,6 @@ func makeTestContentRecordDecl(named name: TokenSyntax, in typeName: TypeSyntax? @Testing.__testing(warning: "Platform-specific implementation missing: test content section name unavailable") #endif @_used - #endif \(result) """ #endif diff --git a/Sources/TestingMacros/TestDeclarationMacro.swift b/Sources/TestingMacros/TestDeclarationMacro.swift index 8007c3aaf..5f7c1938d 100644 --- a/Sources/TestingMacros/TestDeclarationMacro.swift +++ b/Sources/TestingMacros/TestDeclarationMacro.swift @@ -13,10 +13,6 @@ public import SwiftSyntax import SwiftSyntaxBuilder public import SwiftSyntaxMacros -#if !hasFeature(SymbolLinkageMarkers) && SWT_NO_LEGACY_TEST_DISCOVERY -#error("Platform-specific misconfiguration: either SymbolLinkageMarkers or legacy test discovery is required to expand @Test") -#endif - /// A type describing the expansion of the `@Test` attribute macro. /// /// This type is used to implement the `@Test` attribute macro. Do not use it @@ -493,21 +489,6 @@ public struct TestDeclarationMacro: PeerMacro, Sendable { ) ) -#if !SWT_NO_LEGACY_TEST_DISCOVERY - // Emit a type that contains a reference to the test content record. - let enumName = context.makeUniqueName(thunking: functionDecl, withPrefix: "__🟡$") - result.append( - """ - @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.") - enum \(enumName): Testing.__TestContentRecordContainer { - nonisolated static var __testContentRecord: Testing.__TestContentRecord { - unsafe \(testContentRecordName) - } - } - """ - ) -#endif - return result } } diff --git a/Sources/_TestDiscovery/DiscoverableAsTestContent.swift b/Sources/_TestDiscovery/DiscoverableAsTestContent.swift index 16369f82a..0484dd8af 100644 --- a/Sources/_TestDiscovery/DiscoverableAsTestContent.swift +++ b/Sources/_TestDiscovery/DiscoverableAsTestContent.swift @@ -39,19 +39,4 @@ public protocol DiscoverableAsTestContent: Sendable { /// By default, this type equals `Never`, indicating that this type of test /// content does not support hinting during discovery. associatedtype TestContentAccessorHint = Never - -#if !SWT_NO_LEGACY_TEST_DISCOVERY - /// A string present in the names of types containing test content records - /// associated with this type. - @available(swift, deprecated: 100000.0, message: "Do not adopt this functionality in new code. It will be removed in a future release.") - static var _testContentTypeNameHint: String { get } -#endif -} - -#if !SWT_NO_LEGACY_TEST_DISCOVERY -extension DiscoverableAsTestContent { - public static var _testContentTypeNameHint: String { - "__🟡$" - } } -#endif diff --git a/Sources/_TestDiscovery/SectionBounds.swift b/Sources/_TestDiscovery/SectionBounds.swift index 801dc7508..18c05f222 100644 --- a/Sources/_TestDiscovery/SectionBounds.swift +++ b/Sources/_TestDiscovery/SectionBounds.swift @@ -26,11 +26,6 @@ struct SectionBounds: Sendable, BitwiseCopyable { enum Kind: Int, Equatable, Hashable, CaseIterable { /// 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. @@ -61,10 +56,6 @@ extension SectionBounds.Kind { switch self { case .testContent: ("__DATA_CONST", "__swift5_tests") -#if !SWT_NO_LEGACY_TEST_DISCOVERY - case .typeMetadata: - ("__TEXT", "__swift5_types") -#endif } } } @@ -198,10 +189,6 @@ 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) @@ -293,10 +280,6 @@ private func _sectionBounds(_ kind: SectionBounds.Kind) -> some Sequence
CollectionOfOne } + .map { records.baseAddress! + $0 } .filter { $0.pointee.kind == kind } .map { TestContentRecord(imageAddress: sb.imageAddress, recordAddress: $0) } } } } } - -#if !SWT_NO_LEGACY_TEST_DISCOVERY -// MARK: - Legacy test content discovery - -private import _TestingInternals - -extension DiscoverableAsTestContent { - /// Get all test content of this type known to Swift and found in the current - /// process using the legacy discovery mechanism. - /// - /// - Parameters: - /// - baseType: The type which all discovered container types must - /// conform to or subclass. - /// - loader: A function that is called once per type conforming to or - /// subclassing `baseType`. This function should load the corresponding - /// test content record into the buffer passed to it. - /// - /// - Returns: A sequence of instances of ``TestContentRecord``. Only test - /// content records matching this ``TestContent`` type's requirements are - /// included in the sequence. - @available(swift, deprecated: 100000.0, message: "Do not adopt this functionality in new code. It will be removed in a future release.") - public static func allTypeMetadataBasedTestContentRecords( - loadingWith loader: @escaping @Sendable (Any.Type, UnsafeMutableRawBufferPointer) -> Bool - ) -> some Sequence> { - validateMemoryLayout() - - let typeNameHint = _testContentTypeNameHint - let kind = testContentKind.rawValue - let loader: @Sendable (Any.Type) -> _TestContentRecord? = { type in - withUnsafeTemporaryAllocation(of: _TestContentRecord.self, capacity: 1) { buffer in - // Load the record from the container type. - guard loader(type, .init(buffer)) else { - return nil - } - return buffer.baseAddress!.move() - } - } - - return SectionBounds.all(.typeMetadata).lazy.flatMap { sb in - stride(from: 0, to: sb.buffer.count, by: SWTTypeMetadataRecordByteCount).lazy - .map { sb.buffer.baseAddress! + $0 } - .compactMap { swt_getType(fromTypeMetadataRecord: $0, ifNameContains: typeNameHint) } - .map { unsafeBitCast($0, to: Any.Type.self) } - .compactMap(loader) - .filter { $0.kind == kind } - .map { TestContentRecord(imageAddress: sb.imageAddress, record: $0) } - } - } -} -#endif diff --git a/Sources/_TestingInternals/CMakeLists.txt b/Sources/_TestingInternals/CMakeLists.txt index b2bc5b6b1..6e8e88a3c 100644 --- a/Sources/_TestingInternals/CMakeLists.txt +++ b/Sources/_TestingInternals/CMakeLists.txt @@ -11,7 +11,6 @@ set(CMAKE_CXX_SCAN_FOR_MODULES 0) include(GitCommit) include(TargetTriple) add_library(_TestingInternals STATIC - Discovery.cpp ExecutablePath.cpp Versions.cpp WillThrow.cpp) diff --git a/Sources/_TestingInternals/Discovery.cpp b/Sources/_TestingInternals/Discovery.cpp deleted file mode 100644 index 1e70a038d..000000000 --- a/Sources/_TestingInternals/Discovery.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023–2025 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 -// - -#include "Discovery.h" - -#if !defined(SWT_NO_LEGACY_TEST_DISCOVERY) -#include -#include -#include - -#pragma mark - Swift ABI - -#if defined(__PTRAUTH_INTRINSICS__) -#include -#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR __ptrauth(ptrauth_key_process_independent_data, 1, 0xae86) -#else -#define SWT_PTRAUTH_SWIFT_TYPE_DESCRIPTOR -#endif - -/// A type representing a pointer relative to itself. -/// -/// This type is derived from `RelativeDirectPointerIntPair` in the Swift -/// repository. -template -struct SWTRelativePointer { -private: - int32_t _offset; - -public: - SWTRelativePointer(const SWTRelativePointer&) = delete; - SWTRelativePointer(const SWTRelativePointer&&) = delete; - SWTRelativePointer& operator =(const SWTRelativePointer&) = delete; - SWTRelativePointer& operator =(const SWTRelativePointer&&) = delete; - - int32_t getRawValue(void) const { - return _offset; - } - - const T *_Nullable get(void) const& { - int32_t maskedOffset = getRawValue() & ~maskValue; - if (maskedOffset == 0) { - return nullptr; - } - - auto offset = static_cast(static_cast(maskedOffset)); - auto result = reinterpret_cast(reinterpret_cast(this) + offset); -#if defined(__PTRAUTH_INTRINSICS__) - if (std::is_function_v && result) { - result = ptrauth_strip(result, ptrauth_key_function_pointer); - result = ptrauth_sign_unauthenticated(result, ptrauth_key_function_pointer, 0); - } -#endif - return reinterpret_cast(result); - } -}; - -/// A type representing a 32-bit absolute function pointer, usually used on platforms -/// where relative function pointers are not supported. -/// -/// This type is derived from `AbsoluteFunctionPointer` in the Swift repository. -template -struct SWTAbsoluteFunctionPointer { -private: - T *_pointer; - static_assert(sizeof(T *) == sizeof(int32_t), "Function pointer must be 32-bit when using compact absolute pointer"); - -public: - const T *_Nullable get(void) const & { - return _pointer; - } -}; - -/// A type representing a pointer relative to itself with low bits reserved for -/// use as flags. -/// -/// This type is derived from `RelativeDirectPointerIntPair` in the Swift -/// repository. -template -struct SWTRelativePointerIntPair: public SWTRelativePointer { - I getInt() const & { - return I(this->getRawValue() & maskValue); - } -}; - -template -#if defined(__wasm32__) -using SWTCompactFunctionPointer = SWTAbsoluteFunctionPointer; -#else -using SWTCompactFunctionPointer = SWTRelativePointer; -#endif - -/// A type representing a metatype as constructed during compilation of a Swift -/// module. -/// -/// This type is derived from `TargetTypeContextDescriptor` in the Swift -/// repository. -struct SWTTypeContextDescriptor { -private: - uint32_t _flags; - SWTRelativePointer _parent; - SWTRelativePointer _name; - - struct MetadataAccessResponse { - void *value; - size_t state; - }; - using MetadataAccessFunction = __attribute__((swiftcall)) MetadataAccessResponse(size_t); - SWTCompactFunctionPointer _metadataAccessFunction; - -public: - const char *_Nullable getName(void) const& { - return _name.get(); - } - - void *_Nullable getMetadata(void) const& { - if (auto fp = _metadataAccessFunction.get()) { - return (* fp)(0xFF).value; - } - return nullptr; - } - - bool isGeneric(void) const& { - return (_flags & 0x80u) != 0; - } -}; - -/// A type representing a relative pointer to a type descriptor. -/// -/// This type is derived from `TargetTypeMetadataRecord` in the Swift -/// repository. -struct SWTTypeMetadataRecord { -private: - SWTRelativePointerIntPair _pointer; - -public: - const SWTTypeContextDescriptor *_Nullable getContextDescriptor(void) const { - switch (_pointer.getInt()) { - case 0: // Direct pointer. - return reinterpret_cast(_pointer.get()); - case 1: // Indirect pointer (pointer to a pointer.) - // The inner pointer is signed when pointer authentication - // instructions are available. - if (auto contextDescriptor = reinterpret_cast(_pointer.get())) { - return *contextDescriptor; - } - [[fallthrough]]; - default: // Unsupported or invalid. - return nullptr; - } - } -}; - -#pragma mark - Legacy test discovery - -const size_t SWTTypeMetadataRecordByteCount = sizeof(SWTTypeMetadataRecord); - -const void *swt_getTypeFromTypeMetadataRecord(const void *recordAddress, const char *nameSubstring) { - auto record = reinterpret_cast(recordAddress); - auto contextDescriptor = record->getContextDescriptor(); - if (!contextDescriptor) { - // This type metadata record is invalid (or we don't understand how to - // get its context descriptor), so skip it. - return nullptr; - } else if (contextDescriptor->isGeneric()) { - // Generic types cannot be fully instantiated without generic - // parameters, which is not something we can know abstractly. - return nullptr; - } - - // Check that the type's name passes. This will be more expensive than the - // checks above, but should be cheaper than realizing the metadata. - const char *typeName = contextDescriptor->getName(); - bool nameOK = typeName && nullptr != std::strstr(typeName, nameSubstring); - if (!nameOK) { - return nullptr; - } - - if (void *typeMetadata = contextDescriptor->getMetadata()) { - return typeMetadata; - } - - return nullptr; -} -#endif diff --git a/Sources/_TestingInternals/include/Discovery.h b/Sources/_TestingInternals/include/Discovery.h deleted file mode 100644 index 25c3603b3..000000000 --- a/Sources/_TestingInternals/include/Discovery.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023–2025 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 -// - -#if !defined(SWT_DISCOVERY_H) -#define SWT_DISCOVERY_H - -#include "Defines.h" -#include "Includes.h" - -SWT_ASSUME_NONNULL_BEGIN - -#if defined(__ELF__) && defined(__swift__) -#pragma mark - ELF image enumeration - -/// A function exported by the Swift runtime that enumerates all metadata -/// sections loaded into the current process. -/// -/// This function is needed on ELF-based platforms because they do not preserve -/// section information that we can discover at runtime. -SWT_IMPORT_FROM_STDLIB void swift_enumerateAllMetadataSections( - bool (* body)(const void *sections, void *context), - void *context -); -#endif - -#pragma mark - Legacy test discovery - -/// The size, in bytes, of a Swift type metadata record. -SWT_EXTERN const size_t SWTTypeMetadataRecordByteCount; - -/// Get the type represented by the type metadata record at the given address if -/// its name contains the given string. -/// -/// - Parameters: -/// - recordAddress: The address of the Swift type metadata record. -/// - nameSubstring: A string which the names of matching types contain. -/// -/// - Returns: A Swift metatype (as `const void *`) or `nullptr` if it wasn't a -/// usable type metadata record or its name did not contain `nameSubstring`. -SWT_EXTERN const void *_Nullable swt_getTypeFromTypeMetadataRecord( - const void *recordAddress, - const char *nameSubstring -) SWT_SWIFT_NAME(swt_getType(fromTypeMetadataRecord:ifNameContains:)); - -SWT_ASSUME_NONNULL_END - -#endif diff --git a/Sources/_TestingInternals/include/Stubs.h b/Sources/_TestingInternals/include/Stubs.h index 636ea9aff..9ba92fc06 100644 --- a/Sources/_TestingInternals/include/Stubs.h +++ b/Sources/_TestingInternals/include/Stubs.h @@ -126,6 +126,20 @@ static char *_Nullable *_Null_unspecified swt_environ(void) { } #endif +#if defined(__ELF__) && defined(__swift__) +#pragma mark - ELF image enumeration + +/// A function exported by the Swift runtime that enumerates all metadata +/// sections loaded into the current process. +/// +/// This function is needed on ELF-based platforms because they do not preserve +/// section information that we can discover at runtime. +SWT_IMPORT_FROM_STDLIB void swift_enumerateAllMetadataSections( + bool (* body)(const void *sections, void *context), + void *context +); +#endif + #if !defined(__ANDROID__) #if __has_include() && defined(si_pid) /// Get the value of the `si_pid` field of a `siginfo_t` structure. diff --git a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift index d2c2efa6e..fc54034a6 100644 --- a/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift +++ b/Tests/TestingMacrosTests/TestDeclarationMacroTests.swift @@ -472,12 +472,12 @@ struct TestDeclarationMacroTests { func differentFunctionTypes(input: String, expectedTypeName: String?, otherCode: String?) throws { let (output, _) = try parse(input) -#if hasFeature(SymbolLinkageMarkers) +#if compiler(>=6.3) + #expect(output.contains("@section")) +#else #expect(output.contains("@_section")) #endif -#if !SWT_NO_LEGACY_TEST_DISCOVERY - #expect(output.contains("__TestContentRecordContainer")) -#endif + #expect(!output.contains("__TestContentRecordContainer")) if let expectedTypeName { #expect(output.contains(expectedTypeName)) } diff --git a/Tests/TestingTests/DiscoveryTests.swift b/Tests/TestingTests/DiscoveryTests.swift index 24d2eecfa..c77ca914a 100644 --- a/Tests/TestingTests/DiscoveryTests.swift +++ b/Tests/TestingTests/DiscoveryTests.swift @@ -58,7 +58,7 @@ struct DiscoveryTests { } #endif -#if !SWT_NO_DYNAMIC_LINKING && hasFeature(SymbolLinkageMarkers) +#if !SWT_NO_DYNAMIC_LINKING struct MyTestContent: DiscoverableAsTestContent { typealias TestContentAccessorHint = UInt32 @@ -80,6 +80,18 @@ struct DiscoveryTests { record.context } +#if compiler(>=6.3) +#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 +#else #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) @@ -90,6 +102,7 @@ struct DiscoveryTests { @__testing(warning: "Platform-specific implementation missing: test content section name unavailable") #endif @_used +#endif private static let record: __TestContentRecord = ( 0xABCD1234, 0, @@ -105,7 +118,7 @@ struct DiscoveryTests { _ = outValue.initializeMemory(as: Self.self, to: .init(value: expectedValue)) return true }, - UInt(truncatingIfNeeded: UInt64(0x0204060801030507)), + 0x02040608, 0 ) } @@ -143,21 +156,4 @@ struct DiscoveryTests { }) } #endif - -#if !SWT_NO_LEGACY_TEST_DISCOVERY && hasFeature(SymbolLinkageMarkers) - @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 }