Skip to content

Commit

Permalink
Merge pull request swiftlang#25030 from jckarter/SR-10600-back-deploy
Browse files Browse the repository at this point in the history
Introduce a backward-deployment library for SR-10600.
  • Loading branch information
jckarter authored May 29, 2019
2 parents 4c65f6f + 2a2d40d commit 2a28948
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 19 deletions.
4 changes: 4 additions & 0 deletions include/swift/Option/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -888,4 +888,8 @@ def vfsoverlay : JoinedOrSeparate<["-"], "vfsoverlay">,
def vfsoverlay_EQ : Joined<["-"], "vfsoverlay=">,
Alias<vfsoverlay>;

// Runtime compatibility version
def runtime_compatibility_version : Separate<["-"], "runtime-compatibility-version">,
Flags<[FrontendOption]>,
HelpText<"Link compatibility library for Swift runtime version, or 'none'">;
include "FrontendOptions.td"
71 changes: 70 additions & 1 deletion lib/Driver/DarwinToolChains.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,46 @@ static bool wantsObjCRuntime(const llvm::Triple &triple) {
llvm_unreachable("unknown Darwin OS");
}

/// Return the earliest backward deployment compatibility version we need to
/// link in for the given target triple, if any.
static Optional<std::pair<unsigned, unsigned>>
getSwiftRuntimeCompatibilityVersionForTarget(const llvm::Triple &Triple) {
unsigned Major, Minor, Micro;

if (Triple.isMacOSX()) {
Triple.getMacOSXVersion(Major, Minor, Micro);
if (Major == 10) {
if (Minor <= 14) {
return std::make_pair(5u, 0u);
} else {
return None;
}
} else {
return None;
}
} else if (Triple.isiOS()) { // includes tvOS
Triple.getiOSVersion(Major, Minor, Micro);
if (Major <= 12) {
return std::make_pair(5u, 0u);
} else {
return None;
}
} else if (Triple.isWatchOS()) {
Triple.getWatchOSVersion(Major, Minor, Micro);
if (Major <= 5) {
return std::make_pair(5u, 0u);
} else {
return None;
}
} else {
return None;
}
}

ToolChain::InvocationInfo
toolchains::Darwin::constructInvocation(const LinkJobAction &job,
const JobContext &context) const {
const JobContext &context) const
{
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
"Invalid linker output type.");

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

// Link compatibility libraries, if we're deploying back to OSes that
// have an older Swift runtime.
Optional<std::pair<unsigned, unsigned>> runtimeCompatibilityVersion;

if (context.Args.hasArg(options::OPT_runtime_compatibility_version)) {
auto value = context.Args.getLastArgValue(options::OPT_runtime_compatibility_version);
if (value.equals("5.0")) {
runtimeCompatibilityVersion = std::make_pair(5u, 0u);
} else if (value.equals("none")) {
runtimeCompatibilityVersion = None;
} else {
// TODO: diagnose unknown runtime compatibility version?
}
} else if (job.getKind() == LinkKind::Executable) {
runtimeCompatibilityVersion
= getSwiftRuntimeCompatibilityVersionForTarget(Triple);
}

if (runtimeCompatibilityVersion) {
if (*runtimeCompatibilityVersion <= std::make_pair(5u, 0u)) {
// Swift 5.0 compatibility library
SmallString<128> BackDeployLib;
BackDeployLib.append(RuntimeLibPath);
llvm::sys::path::append(BackDeployLib, "libswiftCompatibility50.a");

if (llvm::sys::fs::exists(BackDeployLib)) {
Arguments.push_back("-force_load");
Arguments.push_back(context.Args.MakeArgString(BackDeployLib));
}
}
}

// Link the standard library.
Arguments.push_back("-L");
if (context.Args.hasFlag(options::OPT_static_stdlib,
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ if(SWIFT_BUILD_STDLIB)
add_subdirectory(stubs)
add_subdirectory(core)
add_subdirectory(SwiftOnoneSupport)
add_subdirectory(Compatibility50)
endif()

if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR)
Expand Down
10 changes: 10 additions & 0 deletions stdlib/public/Compatibility50/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set(swift_runtime_compile_flags ${SWIFT_RUNTIME_CORE_CXX_FLAGS})
set(swift_runtime_linker_flags ${SWIFT_RUNTIME_CORE_LINK_FLAGS})

add_swift_target_library(swiftCompatibility50 TARGET_LIBRARY STATIC
ProtocolConformance.cpp
Overrides.cpp
C_COMPILE_FLAGS ${swift_runtime_library_compile_flags}
LINK_FLAGS ${swift_runtime_linker_flags}
TARGET_SDKS ${SWIFT_APPLE_PLATFORMS}
INSTALL_IN_COMPONENT stdlib)
33 changes: 33 additions & 0 deletions stdlib/public/Compatibility50/Overrides.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===--- Overrides.cpp - Compat override table for Swift 5.0 runtime ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides compatibility override hooks for Swift 5.0 runtimes.
//
//===----------------------------------------------------------------------===//

#include "Overrides.h"
#include "../runtime/CompatibilityOverride.h"

using namespace swift;

struct OverrideSection {
uintptr_t version;
#define OVERRIDE(name, ret, attrs, ccAttrs, namespace, typedArgs, namedArgs) \
Override_ ## name name;
#include "../runtime/CompatibilityOverride.def"
};

OverrideSection Overrides
__attribute__((used, section("__DATA,__swift_hooks"))) = {
.version = 0,
.conformsToProtocol = swift50override_conformsToProtocol,
};
30 changes: 30 additions & 0 deletions stdlib/public/Compatibility50/Overrides.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===--- Overrides.cpp - Compat overrides for Swift 5.0 runtime ----s------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides compatibility override hooks for Swift 5.0 runtimes.
//
//===----------------------------------------------------------------------===//

#include "swift/Basic/LLVM.h"
#include "swift/Runtime/Metadata.h"

namespace swift {

using ConformsToProtocol_t =
const WitnessTable *(const Metadata *, const ProtocolDescriptor *);

const WitnessTable *
swift50override_conformsToProtocol(const Metadata * const type,
const ProtocolDescriptor *protocol,
ConformsToProtocol_t *original);

}
71 changes: 71 additions & 0 deletions stdlib/public/Compatibility50/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===--- ProtocolConformance.cpp - Swift protocol conformance checking ----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Checking and caching of Swift protocol conformances.
//
// This implementation is intended to be backward-deployed into Swift 5.0
// runtimes.
//
//===----------------------------------------------------------------------===//

#include "Overrides.h"
#include "../runtime/Private.h"
#include "swift/Basic/Lazy.h"
#include <objc/runtime.h>

using namespace swift;

// Clone of private function getRootSuperclass. This returns the SwiftObject
// class in the ABI-stable dylib, regardless of what the local runtime build
// does, since we're always patching an ABI-stable dylib.
__attribute__((visibility("hidden"), weak))
const ClassMetadata *swift::getRootSuperclass() {
auto theClass = SWIFT_LAZY_CONSTANT(objc_getClass("_TtCs12_SwiftObject"));
return (const ClassMetadata *)theClass;
}

// Clone of private helper swift::_swiftoverride_class_getSuperclass
// for use in the override implementation.
static const Metadata *_swift50override_class_getSuperclass(
const Metadata *theClass) {
if (const ClassMetadata *classType = theClass->getClassObject()) {
if (classHasSuperclass(classType))
return getMetadataForClass(classType->Superclass);
}

if (const ForeignClassMetadata *foreignClassType
= dyn_cast<ForeignClassMetadata>(theClass)) {
if (const Metadata *superclass = foreignClassType->Superclass)
return superclass;
}

return nullptr;
}

const WitnessTable *
swift::swift50override_conformsToProtocol(const Metadata *type,
const ProtocolDescriptor *protocol,
ConformsToProtocol_t *original_conformsToProtocol)
{
// The implementation of swift_conformsToProtocol in Swift 5.0 would return
// a false negative answer when asking whether a subclass conforms using
// a conformance from a superclass. Work around this by walking up the
// superclass chain in cases where the original implementation returns
// null.
do {
auto result = original_conformsToProtocol(type, protocol);
if (result)
return result;
} while ((type = _swift50override_class_getSuperclass(type)));

return nullptr;
}
18 changes: 0 additions & 18 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3850,24 +3850,6 @@ static const WitnessTable *_getForeignWitnessTable(
/*** Other metadata routines ***********************************************/
/***************************************************************************/

template<> const ClassMetadata *
Metadata::getClassObject() const {
switch (getKind()) {
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
return static_cast<const ClassMetadata *>(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}

template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buffer) const {
auto *vwt = getValueWitnesses();
if (vwt->isValueInline())
Expand Down
18 changes: 18 additions & 0 deletions stdlib/public/runtime/Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,24 @@ class TypeInfo {
return c;
#endif
}

template<> inline const ClassMetadata *
Metadata::getClassObject() const {
switch (getKind()) {
case MetadataKind::Class: {
// Native Swift class metadata is also the class object.
return static_cast<const ClassMetadata *>(this);
}
case MetadataKind::ObjCClassWrapper: {
// Objective-C class objects are referenced by their Swift metadata wrapper.
auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this);
return wrapper->Class;
}
// Other kinds of types don't have class objects.
default:
return nullptr;
}
}

void *allocateMetadata(size_t size, size_t align);

Expand Down
22 changes: 22 additions & 0 deletions test/Interpreter/SR-10600.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %target-run-simple-swift | %FileCheck %s

public class BaseView { }
public class GenericView<T>: BaseView { }
public class FinalView: GenericView<ContentForTheView> { }


public class ContentForTheView { }
extension ContentForTheView: InfoNeededByControllers { }

public protocol ConditionallyConformed { }
public protocol InfoNeededByControllers { }

extension GenericView: ConditionallyConformed where T: InfoNeededByControllers { }

open class BaseGenericController<T> where T: BaseView & ConditionallyConformed { }


open class FinalController: BaseGenericController<FinalView> { public override init() { } }

// CHECK: FinalController
print(FinalController())
1 change: 1 addition & 0 deletions validation-test/stdlib/MicroStdlib/Inputs/RuntimeStubs.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ void swift_storeEnumTagSinglePayloadGeneric(void) {}
void swift_retain(){}
void swift_allocBox(){}
void swift_getWitnessTable(void) {}
void swift_getObjCClassMetadata(void) {}

0 comments on commit 2a28948

Please sign in to comment.