Protocol conformance cache for generic types

This change adds a new type of cache (cache by type descriptor) to the protocol conformance lookup system. This optimization is beneficial for generic types, where the
same conformance can be reused across different instantiations of the generic type.

Key changes:
- Add a `GetOrInsertManyScope` class to `ConcurrentReadableHashMap` for performing
  multiple insertions under a single lock
- Add type descriptor-based caching for protocol conformances
- Add environment variables for controlling and debugging the conformance cache
- Add tests to verify the behavior of the conformance cache
- Fix for https://github.com/swiftlang/swift/issues/82889

The implementation is controlled by the `SWIFT_DEBUG_ENABLE_CACHE_PROTOCOL_CONFORMANCES_BY_TYPE_DESCRIPTOR`
environment variable, which is enabled by default.

This reapplies https://github.com/swiftlang/swift/pull/82818 after it's been reverted in https://github.com/swiftlang/swift/pull/83770.
This commit is contained in:
Dmitrii Galimzianov
2025-08-28 23:47:41 +02:00
parent 44ba366759
commit 091b005a60
8 changed files with 550 additions and 114 deletions

View File

@@ -0,0 +1,51 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -o %t/a.out
// RUN: env SWIFT_DEBUG_ENABLE_PROTOCOL_CONFORMANCES_LOOKUP_LOG=1 %target-run %t/a.out 2>&1 | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: objc_interop
// REQUIRES: swift_stdlib_asserts
// UNSUPPORTED: DARWIN_SIMULATOR=ios
// UNSUPPORTED: DARWIN_SIMULATOR=tvos
// UNSUPPORTED: DARWIN_SIMULATOR=watchos
// UNSUPPORTED: DARWIN_SIMULATOR=xros
// UNSUPPORTED: use_os_stdlib
// The optimizer will remove many of these conformance checks due to statically
// knowing the result.
// UNSUPPORTED: swift_test_mode_optimize
// UNSUPPORTED: swift_test_mode_optimize_size
import Foundation
protocol Proto {}
extension Proto {
static var selfType: Any.Type { Self.self }
}
func conformsToProto<T>(_ type: T.Type) -> Bool {
(type as? Proto.Type)?.selfType == type
}
func doesNotConformToProto<T>(_ type: T) -> Bool {
(type as? Proto.Type) == nil
}
@objc class BaseClass: NSObject, Proto {}
@objc class DerivedClass: BaseClass {}
@objc class NonConformingClass: NSObject {}
// CHECK: Check confomance a.BaseClass to Proto: found, source: section scan
assert(conformsToProto(BaseClass.self))
// CHECK: Check confomance a.BaseClass to Proto: found, source: cache by type metadata
assert(conformsToProto(BaseClass.self))
// CHECK: Check confomance a.DerivedClass to Proto: found, source: section scan
assert(conformsToProto(DerivedClass.self))
// CHECK: Check confomance a.DerivedClass to Proto: found, source: cache by type metadata
assert(conformsToProto(DerivedClass.self))
// CHECK: Check confomance a.NonConformingClass to Proto: not found, source: section scan
assert(doesNotConformToProto(NonConformingClass.self))
// CHECK: Check confomance a.NonConformingClass to Proto: not found, source: cache by type metadata
assert(doesNotConformToProto(NonConformingClass.self))

View File

@@ -0,0 +1,121 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -o %t/a.out
// RUN: env SWIFT_DEBUG_ENABLE_PROTOCOL_CONFORMANCES_LOOKUP_LOG=1 %target-run %t/a.out 2>&1 | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: swift_stdlib_asserts
// UNSUPPORTED: DARWIN_SIMULATOR=ios
// UNSUPPORTED: DARWIN_SIMULATOR=tvos
// UNSUPPORTED: DARWIN_SIMULATOR=watchos
// UNSUPPORTED: DARWIN_SIMULATOR=xros
// UNSUPPORTED: use_os_stdlib
// The optimizer will remove many of these conformance checks due to statically
// knowing the result.
// UNSUPPORTED: swift_test_mode_optimize
// UNSUPPORTED: swift_test_mode_optimize_size
protocol Proto {}
extension Proto {
static var selfType: Any.Type { Self.self }
}
func conformsToProto<T>(_ type: T.Type) -> Bool {
(type as? Proto.Type)?.selfType == type
}
func doesNotConformToProto<T>(_ type: T) -> Bool {
(type as? Proto.Type) == nil
}
extension Array: Proto {}
extension Dictionary: Proto where Key: Proto {}
extension Int: Proto {}
// CHECK: Check confomance Swift.Int to Proto: found, source: section scan
assert(conformsToProto(Int.self))
// CHECK: Check confomance Swift.Int to Proto: found, source: cache by type metadata
assert(conformsToProto(Int.self))
// CHECK: Check confomance Swift.String to Proto: not found, source: section scan
assert(doesNotConformToProto(String.self))
// CHECK: Check confomance Swift.String to Proto: not found, source: cache by type metadata
assert(doesNotConformToProto(String.self))
// CHECK: Check confomance Swift.Array<Swift.Int> to Proto: found, source: section scan
assert(conformsToProto([Int].self))
// CHECK: Check confomance Swift.Array<Swift.Int> to Proto: found, source: cache by type metadata
assert(conformsToProto([Int].self))
// CHECK: Check confomance Swift.Array<Swift.String> to Proto: found, source: cache by type descriptor
assert(conformsToProto([String].self))
// CHECK: Check confomance Swift.Array<Swift.String> to Proto: found, source: cache by type metadata
assert(conformsToProto([String].self))
// CHECK: Check confomance Swift.Dictionary<Swift.Int, Swift.Int> to Proto: found, source: section scan
assert(conformsToProto([Int: Int].self))
// CHECK: Check confomance Swift.Dictionary<Swift.String, Swift.Int> to Proto: not found, source: cache by type descriptor
assert(doesNotConformToProto([String: Int].self))
// CHECK: Check confomance Swift.Dictionary<Swift.String, Swift.Int> to Proto: not found, source: cache by type metadata
assert(doesNotConformToProto([String: Int].self))
class BaseClass: Proto {}
class DerivedClass: BaseClass {}
class GenericClass<T>: Proto {}
class GenericClassConditional<T> {}
extension GenericClassConditional: Proto where T: Proto {}
// CHECK: Check confomance a.BaseClass to Proto: found, source: section scan
assert(conformsToProto(BaseClass.self))
// CHECK: Check confomance a.BaseClass to Proto: found, source: cache by type metadata
assert(conformsToProto(BaseClass.self))
// CHECK: Check confomance a.DerivedClass to Proto: found, source: section scan
assert(conformsToProto(DerivedClass.self))
// CHECK: Check confomance a.DerivedClass to Proto: found, source: cache by type metadata
assert(conformsToProto(DerivedClass.self))
// CHECK: Check confomance a.GenericClass<Swift.Int> to Proto: found, source: section scan
assert(conformsToProto(GenericClass<Int>.self))
// CHECK: Check confomance a.GenericClass<Swift.Int> to Proto: found, source: cache by type metadata
assert(conformsToProto(GenericClass<Int>.self))
// CHECK: Check confomance a.GenericClass<Swift.String> to Proto: found, source: cache by type descriptor
assert(conformsToProto(GenericClass<String>.self))
// CHECK: Check confomance a.GenericClass<Swift.String> to Proto: found, source: cache by type metadata
assert(conformsToProto(GenericClass<String>.self))
// CHECK: Check confomance a.GenericClassConditional<Swift.Int> to Proto: found, source: section scan
assert(conformsToProto(GenericClassConditional<Int>.self))
// CHECK: Check confomance a.GenericClassConditional<Swift.Int> to Proto: found, source: cache by type metadata
assert(conformsToProto(GenericClassConditional<Int>.self))
// CHECK: Check confomance a.GenericClassConditional<Swift.String> to Proto: not found, source: cache by type descriptor
assert(doesNotConformToProto(GenericClassConditional<String>.self))
// CHECK: Check confomance a.GenericClassConditional<Swift.String> to Proto: not found, source: cache by type metadata
assert(doesNotConformToProto(GenericClassConditional<String>.self))
enum Enum: Proto {}
extension Optional: Proto where Wrapped: Proto {}
// CHECK: Check confomance a.Enum to Proto: found, source: section scan
assert(conformsToProto(Enum.self))
// CHECK: Check confomance a.Enum to Proto: found, source: cache by type metadata
assert(conformsToProto(Enum.self))
// CHECK: Check confomance Swift.Optional<a.Enum> to Proto: found, source: section scan
assert(conformsToProto(Enum?.self))
// CHECK: Check confomance Swift.Optional<a.Enum> to Proto: found, source: cache by type metadata
assert(conformsToProto(Enum?.self))
// CHECK: Check confomance Swift.Optional<Swift.Int> to Proto: found, source: cache by type descriptor
assert(conformsToProto(Int?.self))
// CHECK: Check confomance Swift.Optional<Swift.Int> to Proto: found, source: cache by type metadata
assert(conformsToProto(Int?.self))
// CHECK: Check confomance Swift.Optional<Swift.String> to Proto: not found, source: cache by type descriptor
assert(doesNotConformToProto(String?.self))
// CHECK: Check confomance Swift.Optional<Swift.String> to Proto: not found, source: cache by type metadata
assert(doesNotConformToProto(String?.self))