Skip to content

Commit aac942c

Browse files
authored
Merge pull request #35061 from mikeash/protocol-conformance-iteration-order-workaround
[Runtime] Add a disabled workaround for protocol conformance checking to check conformances in reverse order.
2 parents 9671dbf + 9ac3b0e commit aac942c

File tree

6 files changed

+186
-3
lines changed

6 files changed

+186
-3
lines changed

include/swift/Runtime/Bincompat.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===--- Bincompat.h - Binary compatibility checks. -------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Checks for enabling binary compatibility workarounds.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
namespace swift {
18+
19+
namespace runtime {
20+
21+
namespace bincompat {
22+
23+
/// Whether protocol conformance iteration should be reversed, to prefer
24+
/// conformances from images that are later in the list over earlier ones.
25+
bool workaroundProtocolConformanceReverseIteration();
26+
27+
} // namespace bincompat
28+
29+
} // namespace runtime
30+
31+
} // namespace swift

stdlib/public/runtime/Bincompat.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//===--- Bincompat.cpp - Binary compatibility checks. -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Checks for enabling binary compatibility workarounds.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/Runtime/Bincompat.h"
18+
19+
namespace swift {
20+
21+
namespace runtime {
22+
23+
namespace bincompat {
24+
25+
bool workaroundProtocolConformanceReverseIteration() { return false; }
26+
27+
} // namespace bincompat
28+
29+
} // namespace runtime
30+
31+
} // namespace swift

stdlib/public/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ set(swift_runtime_sources
3030
Array.cpp
3131
AutoDiffSupport.cpp
3232
BackDeployment.cpp
33+
Bincompat.cpp
3334
Casting.cpp
3435
CompatibilityOverride.cpp
3536
CygwinPort.cpp

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/Basic/Lazy.h"
1818
#include "swift/Demangling/Demangle.h"
19+
#include "swift/Runtime/Bincompat.h"
1920
#include "swift/Runtime/Casting.h"
2021
#include "swift/Runtime/Concurrent.h"
2122
#include "swift/Runtime/HeapObject.h"
@@ -238,8 +239,11 @@ namespace {
238239
struct ConformanceState {
239240
ConcurrentReadableHashMap<ConformanceCacheEntry> Cache;
240241
ConcurrentReadableArray<ConformanceSection> SectionsToScan;
241-
242+
bool scanSectionsBackwards;
243+
242244
ConformanceState() {
245+
scanSectionsBackwards =
246+
runtime::bincompat::workaroundProtocolConformanceReverseIteration();
243247
initializeProtocolConformanceLookup();
244248
}
245249

@@ -466,8 +470,7 @@ swift_conformsToProtocolImpl(const Metadata *const type,
466470
return found.second;
467471

468472
// Scan conformance records.
469-
auto snapshot = C.SectionsToScan.snapshot();
470-
for (auto &section : snapshot) {
473+
auto processSection = [&](const ConformanceSection &section) {
471474
// Eagerly pull records for nondependent witnesses into our cache.
472475
for (const auto &record : section) {
473476
auto &descriptor = *record.get();
@@ -485,6 +488,15 @@ swift_conformsToProtocolImpl(const Metadata *const type,
485488
C.cacheResult(matchingType, protocol, witness, /*always cache*/ 0);
486489
}
487490
}
491+
};
492+
493+
auto snapshot = C.SectionsToScan.snapshot();
494+
if (C.scanSectionsBackwards) {
495+
for (auto &section : llvm::reverse(snapshot))
496+
processSection(section);
497+
} else {
498+
for (auto &section : snapshot)
499+
processSection(section);
488500
}
489501

490502
// Try the search again to look for the most specific cached conformance.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/newSDK %target-link-sdk-future-version
3+
// RUN: %target-codesign %t/newSDK
4+
// RUN: %target-run %t/newSDK newSDK
5+
// RUN: %target-build-swift %s -o %t/oldSDK %target-link-sdk-2020-version
6+
// RUN: %target-codesign %t/oldSDK
7+
// RUN: %target-run %t/oldSDK oldSDK
8+
9+
// REQUIRES: VENDOR=apple
10+
11+
// Simulators refuse to run binaries built with an SDK newer than the simulator.
12+
// UNSUPPORTED: DARWIN_SIMULATOR=ios
13+
// UNSUPPORTED: DARWIN_SIMULATOR=tvos
14+
// UNSUPPORTED: DARWIN_SIMULATOR=watchos
15+
16+
import Accelerate
17+
import Foundation
18+
import StdlibUnittest
19+
import SwiftShims
20+
21+
22+
extension CFString: Hashable {
23+
static var localHashableCallCount = 0
24+
public var hashValue: Int {
25+
Self.localHashableCallCount += 1
26+
return (self as String).hashValue
27+
}
28+
}
29+
30+
protocol P {
31+
func firstHashValue() -> Int
32+
}
33+
34+
extension Set: P {
35+
func firstHashValue() -> Int {
36+
return first!.hashValue
37+
}
38+
}
39+
40+
@_optimize(none)
41+
func firstHashValue(_ x: P) -> Int {
42+
x.firstHashValue()
43+
}
44+
45+
let osHasWorkaround: Bool
46+
// These are deliberately NOT version 9999, as we don't want to hit the special
47+
// case where development runtimes always return true for 9999. This check needs
48+
// to be false until real version numbers are put in.
49+
if #available(macOS 99990, iOS 99990, tvOS 99990, watchOS 99990, *) {
50+
osHasWorkaround = true
51+
} else {
52+
osHasWorkaround = false
53+
}
54+
55+
let testingOldSDK = CommandLine.arguments.last == "oldSDK"
56+
57+
var tests: TestSuite
58+
59+
if testingOldSDK {
60+
tests = TestSuite("old SDK protocol conformance collision")
61+
tests.test("CFString: Hashable conformance") {
62+
_ = firstHashValue(NSSet(object: "Whatever") as! Set<CFString>)
63+
64+
let expectedCallCount = osHasWorkaround ? 1 : 0
65+
expectEqual(expectedCallCount, CFString.localHashableCallCount)
66+
}
67+
} else {
68+
tests = TestSuite("new SDK protocol conformance collision")
69+
tests.test("CFString: Hashable conformance") {
70+
_ = firstHashValue(NSSet(object: "Whatever") as! Set<CFString>)
71+
72+
expectEqual(0, CFString.localHashableCallCount)
73+
}
74+
}
75+
76+
runAllTests()

test/lit.cfg

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,31 @@ if run_vendor == 'apple':
11071107

11081108
config.otool_classic = ("%s otool-classic" % (xcrun_prefix))
11091109

1110+
SDK_2020_VERSION = {
1111+
'macosx': '11.0',
1112+
'ios': '14.0',
1113+
'maccatalyst': '14.0',
1114+
'tvos': '14.0',
1115+
'watchos': '7.0'
1116+
}
1117+
sdk_2020_version = SDK_2020_VERSION.get(run_os, '')
1118+
linker_os = {
1119+
'iphoneos': 'ios',
1120+
'appletvos': 'tvos',
1121+
'watchos': 'watchos',
1122+
'iphonesimulator': 'ios-simulator',
1123+
'watchsimulator': 'watchos-simulator',
1124+
'appletvsimulator': 'tvos-simulator',
1125+
'macosx': 'macos'
1126+
}.get(config.target_sdk_name, run_os)
1127+
1128+
config.target_link_sdk_2020_version = (
1129+
"-Xlinker -platform_version -Xlinker %s -Xlinker %s -Xlinker %s" %
1130+
(linker_os, sdk_2020_version, sdk_2020_version))
1131+
config.target_link_sdk_future_version = (
1132+
"-Xlinker -platform_version -Xlinker %s -Xlinker %s -Xlinker %s" %
1133+
(linker_os, target_future_version, target_future_version))
1134+
11101135
elif run_os in ['windows-msvc']:
11111136
lit_config.note('Testing Windows ' + config.variant_triple)
11121137
config.environment['NUMBER_OF_PROCESSORS'] = os.environ['NUMBER_OF_PROCESSORS']
@@ -2021,6 +2046,13 @@ config.substitutions.append(('%llvm-cov', config.llvm_cov))
20212046
if hasattr(config, 'otool_classic'):
20222047
config.substitutions.append(('%otool-classic', config.otool_classic))
20232048

2049+
if hasattr(config, 'target_link_sdk_2020_version'):
2050+
config.substitutions.append(('%target-link-sdk-2020-version',
2051+
config.target_link_sdk_2020_version))
2052+
if hasattr(config, 'target_link_sdk_future_version'):
2053+
config.substitutions.append(('%target-link-sdk-future-version',
2054+
config.target_link_sdk_future_version))
2055+
20242056
run_filecheck = '%s %s --sanitize BUILD_DIR=%s --sanitize SOURCE_DIR=%s --use-filecheck %s %s' % (
20252057
shell_quote(sys.executable),
20262058
shell_quote(config.PathSanitizingFileCheck),

0 commit comments

Comments
 (0)