Skip to content

Introduce a backward-deployment library for SR-10600. #25030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: can you use llvm::VersionTuple for this? Bigger, sure, but also obviously a version. (And has an "empty" representation, though the Optional is fine too.)

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you eliminate these else returns? They don't add anything. You can remove the else.

Copy link
Contributor

@gottesmm gottesmm May 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For bonus points: invert if statements to reduce indentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would become unmanageable if we add branches for more OS versions in the future.

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?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-( Yeah, this would have to be somewhere earlier; we currently don't diagnose from command generation.

}
} 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) {}