Skip to content

Commit 2a28948

Browse files
authored
Merge pull request #25030 from jckarter/SR-10600-back-deploy
Introduce a backward-deployment library for SR-10600.
2 parents 4c65f6f + 2a2d40d commit 2a28948

File tree

11 files changed

+260
-19
lines changed

11 files changed

+260
-19
lines changed

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,4 +888,8 @@ def vfsoverlay : JoinedOrSeparate<["-"], "vfsoverlay">,
888888
def vfsoverlay_EQ : Joined<["-"], "vfsoverlay=">,
889889
Alias<vfsoverlay>;
890890

891+
// Runtime compatibility version
892+
def runtime_compatibility_version : Separate<["-"], "runtime-compatibility-version">,
893+
Flags<[FrontendOption]>,
894+
HelpText<"Link compatibility library for Swift runtime version, or 'none'">;
891895
include "FrontendOptions.td"

lib/Driver/DarwinToolChains.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,46 @@ static bool wantsObjCRuntime(const llvm::Triple &triple) {
221221
llvm_unreachable("unknown Darwin OS");
222222
}
223223

224+
/// Return the earliest backward deployment compatibility version we need to
225+
/// link in for the given target triple, if any.
226+
static Optional<std::pair<unsigned, unsigned>>
227+
getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple) {
228+
unsigned Major, Minor, Micro;
229+
230+
if (Triple.isMacOSX()) {
231+
Triple.getMacOSXVersion(Major, Minor, Micro);
232+
if (Major == 10) {
233+
if (Minor <= 14) {
234+
return std::make_pair(5u, 0u);
235+
} else {
236+
return None;
237+
}
238+
} else {
239+
return None;
240+
}
241+
} else if (Triple.isiOS()) { // includes tvOS
242+
Triple.getiOSVersion(Major, Minor, Micro);
243+
if (Major <= 12) {
244+
return std::make_pair(5u, 0u);
245+
} else {
246+
return None;
247+
}
248+
} else if (Triple.isWatchOS()) {
249+
Triple.getWatchOSVersion(Major, Minor, Micro);
250+
if (Major <= 5) {
251+
return std::make_pair(5u, 0u);
252+
} else {
253+
return None;
254+
}
255+
} else {
256+
return None;
257+
}
258+
}
259+
224260
ToolChain::InvocationInfo
225261
toolchains::Darwin::constructInvocation(const LinkJobAction &job,
226-
const JobContext &context) const {
262+
const JobContext &context) const
263+
{
227264
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
228265
"Invalid linker output type.");
229266

@@ -392,6 +429,38 @@ toolchains::Darwin::constructInvocation(const LinkJobAction &job,
392429
SmallString<128> RuntimeLibPath;
393430
getRuntimeLibraryPath(RuntimeLibPath, context.Args, /*Shared=*/true);
394431

432+
// Link compatibility libraries, if we're deploying back to OSes that
433+
// have an older Swift runtime.
434+
Optional<std::pair<unsigned, unsigned>> runtimeCompatibilityVersion;
435+
436+
if (context.Args.hasArg(options::OPT_runtime_compatibility_version)) {
437+
auto value = context.Args.getLastArgValue(options::OPT_runtime_compatibility_version);
438+
if (value.equals("5.0")) {
439+
runtimeCompatibilityVersion = std::make_pair(5u, 0u);
440+
} else if (value.equals("none")) {
441+
runtimeCompatibilityVersion = None;
442+
} else {
443+
// TODO: diagnose unknown runtime compatibility version?
444+
}
445+
} else if (job.getKind() == LinkKind::Executable) {
446+
runtimeCompatibilityVersion
447+
= getSwiftRuntimeCompatibilityVersionForTarget(Triple);
448+
}
449+
450+
if (runtimeCompatibilityVersion) {
451+
if (*runtimeCompatibilityVersion <= std::make_pair(5u, 0u)) {
452+
// Swift 5.0 compatibility library
453+
SmallString<128> BackDeployLib;
454+
BackDeployLib.append(RuntimeLibPath);
455+
llvm::sys::path::append(BackDeployLib, "libswiftCompatibility50.a");
456+
457+
if (llvm::sys::fs::exists(BackDeployLib)) {
458+
Arguments.push_back("-force_load");
459+
Arguments.push_back(context.Args.MakeArgString(BackDeployLib));
460+
}
461+
}
462+
}
463+
395464
// Link the standard library.
396465
Arguments.push_back("-L");
397466
if (context.Args.hasFlag(options::OPT_static_stdlib,

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())

validation-test/stdlib/MicroStdlib/Inputs/RuntimeStubs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ void swift_storeEnumTagSinglePayloadGeneric(void) {}
1717
void swift_retain(){}
1818
void swift_allocBox(){}
1919
void swift_getWitnessTable(void) {}
20+
void swift_getObjCClassMetadata(void) {}

0 commit comments

Comments
 (0)