Skip to content

Commit 9e1907a

Browse files
committed
Introduce a backward-deployment library for SR-10600.
Build a static archive that can be linked into executables and take advantage of the Swift runtime's hooking mechanism to work around the issue Doug fixed in #24759. The Swift 5.0 version of swift_conformsToProtocol would return a false negative in some cases where a subclass conforms using an inherited conformance, so work around this by successively retrying the original implementation up the superclass chain to try to find a match.
1 parent ad79bba commit 9e1907a

File tree

8 files changed

+185
-18
lines changed

8 files changed

+185
-18
lines changed

stdlib/public/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ if(SWIFT_BUILD_STDLIB)
6161
add_subdirectory(stubs)
6262
add_subdirectory(core)
6363
add_subdirectory(SwiftOnoneSupport)
64+
add_subdirectory(Compatibility50)
6465
endif()
6566

6667
if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
set(swift_runtime_compile_flags ${SWIFT_RUNTIME_CORE_CXX_FLAGS})
2+
set(swift_runtime_linker_flags ${SWIFT_RUNTIME_CORE_LINK_FLAGS})
3+
4+
add_swift_target_library(swiftCompatibility50 TARGET_LIBRARY STATIC
5+
ProtocolConformance.cpp
6+
Overrides.cpp
7+
C_COMPILE_FLAGS ${swift_runtime_library_compile_flags}
8+
LINK_FLAGS ${swift_runtime_linker_flags}
9+
TARGET_SDKS ${SWIFT_APPLE_PLATFORMS}
10+
INSTALL_IN_COMPONENT stdlib)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===--- Overrides.cpp - Compat override table for Swift 5.0 runtime ------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
// This file provides compatibility override hooks for Swift 5.0 runtimes.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "Overrides.h"
18+
#include "../runtime/CompatibilityOverride.h"
19+
20+
using namespace swift;
21+
22+
struct OverrideSection {
23+
uintptr_t version;
24+
#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \
25+
Override_ ## name name;
26+
#include "../runtime/CompatibilityOverride.def"
27+
};
28+
29+
OverrideSection Overrides
30+
__attribute__((used, section("__DATA,__swift_hooks"))) = {
31+
.version = 0,
32+
.conformsToProtocol = swift50override_conformsToProtocol,
33+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- Overrides.cpp - Compat overrides for Swift 5.0 runtime ----s------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
// This file provides compatibility override hooks for Swift 5.0 runtimes.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "swift/Basic/LLVM.h"
18+
#include "swift/Runtime/Metadata.h"
19+
20+
namespace swift {
21+
22+
using ConformsToProtocol_t =
23+
const WitnessTable *(const Metadata *, const ProtocolDescriptor *);
24+
25+
const WitnessTable *
26+
swift50override_conformsToProtocol(const Metadata * const type,
27+
const ProtocolDescriptor *protocol,
28+
ConformsToProtocol_t *original);
29+
30+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 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+
// Checking and caching of Swift protocol conformances.
14+
//
15+
// This implementation is intended to be backward-deployed into Swift 5.0
16+
// runtimes.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "Overrides.h"
21+
#include "../runtime/Private.h"
22+
#include "swift/Basic/Lazy.h"
23+
#include <objc/runtime.h>
24+
25+
using namespace swift;
26+
27+
// Clone of private function getRootSuperclass. This returns the SwiftObject
28+
// class in the ABI-stable dylib, regardless of what the local runtime build
29+
// does, since we're always patching an ABI-stable dylib.
30+
__attribute__((visibility("hidden"), weak))
31+
const ClassMetadata *swift::getRootSuperclass() {
32+
auto theClass = SWIFT_LAZY_CONSTANT(objc_getClass("_TtCs12_SwiftObject"));
33+
return (const ClassMetadata *)theClass;
34+
}
35+
36+
// Clone of private helper swift::_swiftoverride_class_getSuperclass
37+
// for use in the override implementation.
38+
static const Metadata *_swift50override_class_getSuperclass(
39+
const Metadata *theClass) {
40+
if (const ClassMetadata *classType = theClass->getClassObject()) {
41+
if (classHasSuperclass(classType))
42+
return getMetadataForClass(classType->Superclass);
43+
}
44+
45+
if (const ForeignClassMetadata *foreignClassType
46+
= dyn_cast<ForeignClassMetadata>(theClass)) {
47+
if (const Metadata *superclass = foreignClassType->Superclass)
48+
return superclass;
49+
}
50+
51+
return nullptr;
52+
}
53+
54+
const WitnessTable *
55+
swift::swift50override_conformsToProtocol(const Metadata *type,
56+
const ProtocolDescriptor *protocol,
57+
ConformsToProtocol_t *original_conformsToProtocol)
58+
{
59+
// The implementation of swift_conformsToProtocol in Swift 5.0 would return
60+
// a false negative answer when asking whether a subclass conforms using
61+
// a conformance from a superclass. Work around this by walking up the
62+
// superclass chain in cases where the original implementation returns
63+
// null.
64+
do {
65+
auto result = original_conformsToProtocol(type, protocol);
66+
if (result)
67+
return result;
68+
} while ((type = _swift50override_class_getSuperclass(type)));
69+
70+
return nullptr;
71+
}

stdlib/public/runtime/Metadata.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3850,24 +3850,6 @@ static const WitnessTable *_getForeignWitnessTable(
38503850
/*** Other metadata routines ***********************************************/
38513851
/***************************************************************************/
38523852

3853-
template<> const ClassMetadata *
3854-
Metadata::getClassObject() const {
3855-
switch (getKind()) {
3856-
case MetadataKind::Class: {
3857-
// Native Swift class metadata is also the class object.
3858-
return static_cast<const ClassMetadata *>(this);
3859-
}
3860-
case MetadataKind::ObjCClassWrapper: {
3861-
// Objective-C class objects are referenced by their Swift metadata wrapper.
3862-
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
3863-
return wrapper->Class;
3864-
}
3865-
// Other kinds of types don't have class objects.
3866-
default:
3867-
return nullptr;
3868-
}
3869-
}
3870-
38713853
template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buffer) const {
38723854
auto *vwt = getValueWitnesses();
38733855
if (vwt->isValueInline())

stdlib/public/runtime/Private.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,24 @@ class TypeInfo {
463463
return c;
464464
#endif
465465
}
466+
467+
template<> inline const ClassMetadata *
468+
Metadata::getClassObject() const {
469+
switch (getKind()) {
470+
case MetadataKind::Class: {
471+
// Native Swift class metadata is also the class object.
472+
return static_cast<const ClassMetadata *>(this);
473+
}
474+
case MetadataKind::ObjCClassWrapper: {
475+
// Objective-C class objects are referenced by their Swift metadata wrapper.
476+
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
477+
return wrapper->Class;
478+
}
479+
// Other kinds of types don't have class objects.
480+
default:
481+
return nullptr;
482+
}
483+
}
466484

467485
void *allocateMetadata(size_t size, size_t align);
468486

test/Interpreter/SR-10600.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-run-simple-swift | %FileCheck %s
2+
3+
public class BaseView { }
4+
public class GenericView<T>: BaseView { }
5+
public class FinalView: GenericView<ContentForTheView> { }
6+
7+
8+
public class ContentForTheView { }
9+
extension ContentForTheView: InfoNeededByControllers { }
10+
11+
public protocol ConditionallyConformed { }
12+
public protocol InfoNeededByControllers { }
13+
14+
extension GenericView: ConditionallyConformed where T: InfoNeededByControllers { }
15+
16+
open class BaseGenericController<T> where T: BaseView & ConditionallyConformed { }
17+
18+
19+
open class FinalController: BaseGenericController<FinalView> { public override init() { } }
20+
21+
// CHECK: FinalController
22+
print(FinalController())

0 commit comments

Comments
 (0)