Files
swift-mirror/test/ModuleInterface/features.swift
John McCall 54c38cbb71 Fix and generalize the printing of suppressible features,
and make `@_unsafeInheritExecutor` a suppressible feature.

Some language features are required in order to parse a
declaration correctly, but some can safely be ignored.
For the latter, we'd like the module interface to simply
contain the declaration twice, once with the feature and
once without.  Some basic support for that was already
added for the SpecializeAttributeWithAvailability feature,
but it didn't interact correctly with required features
that might be checked in the same `#if` clause (it simply
introduced an `#else`), and it wasn't really set up to
allow multiple features to be handled this way.  There
were also a few other places that weren't updated to
handle this, presumably because they never coincided
with a `@_specialize` attribute.

Introduce the concept of a suppressible feature, which
is anything that the ASTPrinter can modify the current
PrintOptions in order to suppress.  Restructure the
printing of compatibility checks so that we can print
the body multiple times with different settings.
Print required feature checks in an outer `#if...#endif`,
then perform a separate `#if...#else...#endif` within
if we have suppressible features.  If there are multiple
suppressible features, check for the most recent first,
on the assumption that it will imply the rest; then
perform subsequent checks with an `#elsif` clause.

This should be a far more solid foundation on which to
build compatibility checks in the future.

`@_unsafeInheritExecutor` needs to be suppressible
because it's been added to some rather important
existing APIs.  Simply suppressing the entire decl will
effectively block old tools from using a new SDK to
build many existing projects (if they've adopted
`async`).  Dropping the attribute changes the semantics
of these functions, but only if the compiler features
the SE-0338 scheduling change; this is a very narrow
window of main-branch development builds of the tools,
none of which were officially released.
2022-02-16 16:58:56 -05:00

181 lines
5.7 KiB
Swift

// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -typecheck -swift-version 5 -module-name FeatureTest -emit-module-interface-path - -enable-library-evolution -disable-availability-checking %s | %FileCheck %s
// REQUIRES: concurrency
// REQUIRES: concurrency
// Ensure that when we emit a Swift interface that makes use of new features,
// the uses of those features are guarded by appropriate #if's that allow older
// compilers to skip over the uses of newer features.
// CHECK: #if compiler(>=5.3) && $SpecializeAttributeWithAvailability
// CHECK: @_specialize(exported: true, kind: full, availability: macOS, introduced: 12; where T == Swift.Int)
// CHECK: public func specializeWithAvailability<T>(_ t: T)
// CHECK: #else
// CHECK: public func specializeWithAvailability<T>(_ t: T)
// CHECK: #endif
@_specialize(exported: true, availability: macOS 12, *; where T == Int)
public func specializeWithAvailability<T>(_ t: T) {
}
// CHECK: #if compiler(>=5.3) && $Actors
// CHECK-NEXT: public actor MyActor
// CHECK: @_semantics("defaultActor") nonisolated final public var unownedExecutor: _Concurrency.UnownedSerialExecutor {
// CHECK-NEXT: get
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: #endif
public actor MyActor {
}
// CHECK: #if compiler(>=5.3) && $Actors
// CHECK-NEXT: extension FeatureTest.MyActor
public extension MyActor {
// CHECK-NOT: $Actors
// CHECK: testFunc
func testFunc() async { }
// CHECK: }
// CHECK-NEXT: #endif
}
// CHECK: #if compiler(>=5.3) && $AsyncAwait
// CHECK-NEXT: globalAsync
// CHECK-NEXT: #endif
public func globalAsync() async { }
// CHECK: @_marker public protocol MP {
// CHECK-NEXT: }
@_marker public protocol MP { }
// CHECK: @_marker public protocol MP2 : FeatureTest.MP {
// CHECK-NEXT: }
@_marker public protocol MP2: MP { }
// CHECK-NOT: #if compiler(>=5.3) && $MarkerProtocol
// CHECK: public protocol MP3 : AnyObject, FeatureTest.MP {
// CHECK-NEXT: }
public protocol MP3: AnyObject, MP { }
// CHECK: extension FeatureTest.MP2 {
// CHECK-NEXT: func inMP2
extension MP2 {
public func inMP2() { }
}
// CHECK: class OldSchool : FeatureTest.MP {
public class OldSchool: MP {
// CHECK: #if compiler(>=5.3) && $AsyncAwait
// CHECK-NEXT: takeClass()
// CHECK-NEXT: #endif
public func takeClass() async { }
}
// CHECK: class OldSchool2 : FeatureTest.MP {
public class OldSchool2: MP {
// CHECK: #if compiler(>=5.3) && $AsyncAwait
// CHECK-NEXT: takeClass()
// CHECK-NEXT: #endif
public func takeClass() async { }
}
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
// CHECK-NEXT: @rethrows public protocol RP
@rethrows public protocol RP {
func f() throws -> Bool
}
// CHECK: public struct UsesRP {
public struct UsesRP {
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
// CHECK-NEXT: public var value: FeatureTest.RP? {
// CHECK-NOT: #if compiler(>=5.3) && $RethrowsProtocol
// CHECK: get
public var value: RP? {
nil
}
}
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
// CHECK-NEXT: public struct IsRP
public struct IsRP: RP {
// CHECK-NEXT: public func f()
public func f() -> Bool { }
// CHECK-NOT: $RethrowsProtocol
// CHECK-NEXT: public var isF:
// CHECK-NEXT: get
public var isF: Bool {
f()
}
}
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
// CHECK-NEXT: public func acceptsRP
public func acceptsRP<T: RP>(_: T) { }
// CHECK-NOT: #if compiler(>=5.3) && $MarkerProtocol
// CHECK: extension Swift.Array : FeatureTest.MP where Element : FeatureTest.MP {
extension Array: FeatureTest.MP where Element : FeatureTest.MP { }
// CHECK: }
// CHECK-NOT: #if compiler(>=5.3) && $MarkerProtocol
// CHECK: extension FeatureTest.OldSchool : Swift.UnsafeSendable {
extension OldSchool: UnsafeSendable { }
// CHECK-NEXT: }
// CHECK: #if compiler(>=5.3) && $GlobalActors
// CHECK-NEXT: @globalActor public struct SomeGlobalActor
@globalActor
public struct SomeGlobalActor {
public static let shared = MyActor()
}
// CHECK: #if compiler(>=5.3) && $AsyncAwait
// CHECK-NEXT: func runSomethingSomewhere
// CHECK-NEXT: #endif
public func runSomethingSomewhere(body: () async -> Void) { }
// CHECK: #if compiler(>=5.3) && $Sendable
// CHECK-NEXT: func runSomethingConcurrently(body: @Sendable () ->
// CHECK-NEXT: #endif
public func runSomethingConcurrently(body: @Sendable () -> Void) { }
// CHECK: #if compiler(>=5.3) && $Actors
// CHECK-NEXT: func stage
// CHECK-NEXT: #endif
public func stage(with actor: MyActor) { }
// CHECK: #if compiler(>=5.3) && $AsyncAwait && $Sendable && $InheritActorContext
// CHECK-NEXT: func asyncIsh
// CHECK-NEXT: #endif
public func asyncIsh(@_inheritActorContext operation: @Sendable @escaping () async -> Void) { }
// CHECK: #if compiler(>=5.3) && $AsyncAwait
// CHECK-NEXT: #if $UnsafeInheritExecutor
// CHECK-NEXT: @_unsafeInheritExecutor public func unsafeInheritExecutor() async
// CHECK-NEXT: #else
// CHECK-NEXT: public func unsafeInheritExecutor() async
// CHECK-NEXT: #endif
// CHECK-NEXT: #endif
@_unsafeInheritExecutor
public func unsafeInheritExecutor() async {}
// CHECK: #if compiler(>=5.3) && $AsyncAwait
// CHECK-NEXT: #if $UnsafeInheritExecutor
// CHECK-NEXT: @_specialize{{.*}}
// CHECK-NEXT: @_unsafeInheritExecutor public func multipleSuppressible<T>(value: T) async
// CHECK-NEXT: #elsif $SpecializeAttributeWithAvailability
// CHECK-NEXT: @_specialize{{.*}}
// CHECK-NEXT: public func multipleSuppressible<T>(value: T) async
// CHECK-NEXT: #else
// CHECK-NEXT: public func multipleSuppressible<T>(value: T) async
// CHECK-NEXT: #endif
// CHECK-NEXT: #endif
@_unsafeInheritExecutor
@_specialize(exported: true, availability: SwiftStdlib 5.1, *; where T == Int)
public func multipleSuppressible<T>(value: T) async {}
// CHECK-NOT: extension FeatureTest.MyActor : Swift.Sendable