diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 1934f7db036f3..53f8d3401a84d 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -888,4 +888,8 @@ def vfsoverlay : JoinedOrSeparate<["-"], "vfsoverlay">, def vfsoverlay_EQ : Joined<["-"], "vfsoverlay=">, Alias; +// 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" diff --git a/lib/Driver/DarwinToolChains.cpp b/lib/Driver/DarwinToolChains.cpp index 1e26ab873fa36..738089e27c954 100644 --- a/lib/Driver/DarwinToolChains.cpp +++ b/lib/Driver/DarwinToolChains.cpp @@ -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> +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."); @@ -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> 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, diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 8b661b508cfc7..6df9dafe9cf09 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -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) diff --git a/stdlib/public/Compatibility50/CMakeLists.txt b/stdlib/public/Compatibility50/CMakeLists.txt new file mode 100644 index 0000000000000..970cae529207a --- /dev/null +++ b/stdlib/public/Compatibility50/CMakeLists.txt @@ -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) diff --git a/stdlib/public/Compatibility50/Overrides.cpp b/stdlib/public/Compatibility50/Overrides.cpp new file mode 100644 index 0000000000000..c88ce2bec1cb1 --- /dev/null +++ b/stdlib/public/Compatibility50/Overrides.cpp @@ -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, +}; diff --git a/stdlib/public/Compatibility50/Overrides.h b/stdlib/public/Compatibility50/Overrides.h new file mode 100644 index 0000000000000..edb120e411f83 --- /dev/null +++ b/stdlib/public/Compatibility50/Overrides.h @@ -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); + +} diff --git a/stdlib/public/Compatibility50/ProtocolConformance.cpp b/stdlib/public/Compatibility50/ProtocolConformance.cpp new file mode 100644 index 0000000000000..16477f50c2ddf --- /dev/null +++ b/stdlib/public/Compatibility50/ProtocolConformance.cpp @@ -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 + +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(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; +} diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 9d5d99baa7151..60e4579780be0 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -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(this); - } - case MetadataKind::ObjCClassWrapper: { - // Objective-C class objects are referenced by their Swift metadata wrapper. - auto wrapper = static_cast(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()) diff --git a/stdlib/public/runtime/Private.h b/stdlib/public/runtime/Private.h index 40066912b2150..e78b653e82b00 100644 --- a/stdlib/public/runtime/Private.h +++ b/stdlib/public/runtime/Private.h @@ -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(this); + } + case MetadataKind::ObjCClassWrapper: { + // Objective-C class objects are referenced by their Swift metadata wrapper. + auto wrapper = static_cast(this); + return wrapper->Class; + } + // Other kinds of types don't have class objects. + default: + return nullptr; + } + } void *allocateMetadata(size_t size, size_t align); diff --git a/test/Interpreter/SR-10600.swift b/test/Interpreter/SR-10600.swift new file mode 100644 index 0000000000000..277f13dd82a87 --- /dev/null +++ b/test/Interpreter/SR-10600.swift @@ -0,0 +1,22 @@ +// RUN: %target-run-simple-swift | %FileCheck %s + +public class BaseView { } +public class GenericView: BaseView { } +public class FinalView: GenericView { } + + +public class ContentForTheView { } +extension ContentForTheView: InfoNeededByControllers { } + +public protocol ConditionallyConformed { } +public protocol InfoNeededByControllers { } + +extension GenericView: ConditionallyConformed where T: InfoNeededByControllers { } + +open class BaseGenericController where T: BaseView & ConditionallyConformed { } + + +open class FinalController: BaseGenericController { public override init() { } } + +// CHECK: FinalController +print(FinalController()) diff --git a/validation-test/stdlib/MicroStdlib/Inputs/RuntimeStubs.c b/validation-test/stdlib/MicroStdlib/Inputs/RuntimeStubs.c index e2bc2233a1ccb..8664fa8f8b92d 100644 --- a/validation-test/stdlib/MicroStdlib/Inputs/RuntimeStubs.c +++ b/validation-test/stdlib/MicroStdlib/Inputs/RuntimeStubs.c @@ -17,3 +17,4 @@ void swift_storeEnumTagSinglePayloadGeneric(void) {} void swift_retain(){} void swift_allocBox(){} void swift_getWitnessTable(void) {} +void swift_getObjCClassMetadata(void) {}